using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.IO;
namespace MQRevo
{
    /// <summary>
    /// MQuest RevoCard
    /// MIFARE card which stores card holder information as well as information on the different features available (e.g. ePurse and loyalty points)
    /// </summary>
    public class RevoCard
    {

        #region "Variables & Properties"
        private Dictionary<int, string> roomList;

        private const byte MIFARE_1K = 0x01;
        private const byte MIFARE_4K = 0x02;
        private byte[] master = { 0x87, 0x6E, 0x51, 0xF2, 0x1D, 0x79 };

        private Database db;
        private bool debug;
        private bool writeToDB;
        private byte _cardType;

        private byte _cardStatus;
        /// <summary>
        /// Current status of the card
        /// </summary>
        public byte cardStatus
        {
            get { return _cardStatus; }
        }

        private String _cardID;
        /// <summary>
        /// 4 byte card ID of MIFARE card
        /// </summary>
        public String cardID
        {
            get { return _cardID; }
        }

        private short _issueMerchantID;
        /// <summary>
        /// Unique ID to identify the merchant who issued the card
        /// </summary>
        public short issueMerchantID
        {
            get { return _issueMerchantID; }
        }

        private DateTime _issueDateTime;
        /// <summary>
        /// Date and time when the card was issued
        /// </summary>
        public DateTime issueDateTime
        {
            get { return _issueDateTime; }
        }

        private int _issueCardID;
        /// <summary>
        /// Unique numeric ID of the card
        /// </summary>
        public int issueCardID
        {
            get { return _issueCardID; }
        }

        private short _issueTerminalID;
        /// <summary>
        /// ID of the terminal where the card was issued
        /// </summary>
        public short issueTerminalID
        {
            get { return _issueTerminalID; }
        }

        private String _cardHolderName;
        /// <summary>
        /// Name of the card holder
        /// </summary>
        public String cardHolderName
        {
            get { return _cardHolderName; }
        }

        private ePurse _purse;
        /// <summary>
        /// Purse data of the card
        /// </summary>
        public ePurse purse
        {
            get { return _purse; }
        }

        private List<LoyaltyData> _loyalty;
        /// <summary>
        /// Loyalty data of the card
        /// </summary>
        public List<LoyaltyData> loyalty
        {
            get { return _loyalty; }
        }

        private DateTime _cardHolderDOB;
        /// <summary>
        /// Card holder's date of birth
        /// </summary>
        public DateTime cardHolderDOB
        {
            get { return _cardHolderDOB.Date; }
        }

        private Char _cardHolderGender;
        /// <summary>
        /// Gender of the card holder
        /// </summary>
        public Char cardHolderGender
        {
            get { return _cardHolderGender; }
        }

        
        private String _cardHolderID;
        /// <summary>
        /// Unique ID for the card holder, e.g. IC or Passport number (max 50 characters)
        /// </summary>
        public String cardHolderID
        {
            get { return _cardHolderID; }
        }

        
        private String _cardHolderPhone;
        /// <summary>
        /// Card holder's phone number (max 20 characters)
        /// </summary>
        public String cardHolderPhone
        {
            get { return _cardHolderPhone; }
        }

        
        private string _cardHolderEmail;
        /// <summary>
        /// Card holder's email address (max 50 characters)
        /// </summary>
        public string cardHolderEmail
        {
            get { return _cardHolderEmail; }

        }

        private byte _roomID;
        /// <summary>
        /// The room the card holder is staying in
        /// </summary>
        public byte roomID
        {
            get { return _roomID; }
            set { _roomID = value; }
        }

        private string _roomName;
        /// <summary>
        /// The name of the room the card holder is staying in (retrieved from settings file roomSettings.txt)
        /// </summary>
        public string roomName
        {
            get { return _roomName; }
            set { _roomName = value; }
        }

        private short _bedID;
        /// <summary>
        /// The bed the card holder is staying in
        /// </summary>
        public short bedID
        {
            get { return _bedID; }
            set { _bedID = value; }
        }

        private long sectorsUsed;
        /// <summary>
        /// Information on the last error encountered during an operation
        /// </summary>
        public String lastErr;
        private short startSector;
        private Dictionary<DirData, short> merchantDict;

        //private byte[] KEYA = new byte[6] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; //Remember to change!!
        private byte[] KEYA = new byte[6] { 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5 }; //Remember to change!!

        #endregion

        #region "Structures"
        private struct MembershipData
        {
            public short merchantID;
            private DateTime _programJoinDate;
            public DateTime programJoinDate
            {
                get { return _programJoinDate.Date; }
                set { _programJoinDate = value.Date; }
            }
            private DateTime _memStartDate;
            public DateTime memStartDate
            {
                get { return _memStartDate.Date; }
                set { _memStartDate = value.Date; }
            }
            private DateTime _memExpiryDate;
            public DateTime memExpiryDate
            {
                get { return _memExpiryDate.Date; }
                set { _memExpiryDate = value.Date; }
            }
            public String memberType;
            public String memberID;
        }

        /// <summary>
        /// Stores loyalty data of the card
        /// </summary>
        public struct LoyaltyData
        {
            /// <summary>
            /// Unique ID of the merchant who activated the loyalty program
            /// </summary>
            public short merchantID;
            public short order;
            
            private string _memberID;
            /// <summary>
            /// ID of the member joining the loyalty program (16 characters alphanumeric)
            /// </summary>
            public string memberID
            {
                get { return _memberID; }
                set { _memberID = value; }
            }

            private DateTime _programJoinDate;
            /// <summary>
            /// Date on which the loyalty program was activated
            /// </summary>
            public DateTime programJoinDate
            {
                get { return _programJoinDate.Date; }
                set { _programJoinDate = value.Date; }
            }
            /// <summary>
            /// Number of points currently in the card
            /// </summary>
            public int points;
            internal short pointsSeq;
            private DateTime _expiryDate;
            /// <summary>
            /// Date at which the points expire
            /// </summary>
            public DateTime expiryDate
            {
                get { return _expiryDate.Date; }
                set { _expiryDate = value.Date; }
            }

        }

        private struct eTicket
        {
            internal short merchantID;
            private DateTime _programJoinDate;
            public DateTime programJoinDate
            {
                get { return _programJoinDate.Date; }
                set { _programJoinDate = value.Date; }
            }
            private DateTime _startDate;
            public DateTime startDate
            {
                get { return _startDate.Date; }
                set { _startDate = value.Date; }
            }
            private DateTime _endDate;
            public DateTime endDate
            {
                get { return _endDate.Date; }
                set { _endDate = value.Date; }
            }
            public String eventName;  //16 characters
            public int ticketNum;
            public String remarks;  //12 characters
        }

        /// <summary>
        /// Stores purse data of the card
        /// </summary>
        public struct ePurse
        {
            /// <summary>
            /// Unique ID of the merchant who activated the ePurse
            /// </summary>
            public short merchantID;

            private string _memberID;
            /// <summary>
            /// ID of the member joining the ePurse program (16 characters alphanumeric)
            /// </summary>
            public string memberID
            {
                get { return _memberID; }
                set { _memberID = value; }
            }

            private DateTime _programJoinDate;
            /// <summary>
            /// Date at which the ePurse program was activated
            /// </summary>
            public DateTime programJoinDate
            {
                get { return _programJoinDate.Date; }
                set { _programJoinDate = value.Date; }
            }
            internal short purseSeqNumber;
            private DateTime _purseExpiry;
            /// <summary>
            /// Date at which the ePurse money expires
            /// </summary>
            public DateTime purseExpiry
            {
                get { return _purseExpiry.Date; }
                set { _purseExpiry = value.Date; }
            }
            /// <summary>
            /// Current amount in the ePurse
            /// </summary>
            public int purseValue;
        }

        private struct StampRally
        {
            public short merchantID;
            private DateTime _programJoinDate;
            public DateTime programJoinDate
            {
                get { return _programJoinDate.Date; }
                set { _programJoinDate = value.Date; }
            }
            public short numStamps;
            private DateTime _stampsExpiry;
            public DateTime stampsExpiry
            {
                get { return _stampsExpiry.Date; }
                set { _stampsExpiry = value.Date; }
            }
        }

        private struct GamingData
        {
            public short merchantID;
            private DateTime _programJoinDate;
            public DateTime programJoinDate
            {
                get { return _programJoinDate.Date; }
                set { _programJoinDate = value.Date; }
            }
            public int Game1Pts;
            public int Game2Pts;
            public int Game3Pts;
            public int Game4Pts;
            public int Game5Pts;
            public int Game6Pts;
            public int Game7Pts;
            public int Game8Pts;
        }

        private struct DirData
        {
            public short merchantID;
            public short programType;
        }
        #endregion

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="start">1st sector of the MIFARE card available to write card information</param>
        public RevoCard(short start)
        {
            if (debug) Log.WriteLog("Invoke Constructor", "RevoCard(" + Convert.ToString(start) + ")");

            lastErr = "";
            startSector = start;
            merchantDict = new Dictionary<DirData, short>();
            _cardHolderEmail = "";
            _cardHolderID = "";
            _cardHolderPhone = "";
            _loyalty = new List<LoyaltyData>();

            debug = Convert.ToBoolean(MQRevo.Properties.Settings.Default["Debug"].ToString());
            writeToDB = Convert.ToBoolean(MQRevo.Properties.Settings.Default["WriteToDB"].ToString());
            roomList = new Dictionary<int,string>();
            //initializeRooms();
            if (writeToDB)
            {
                db = new Database();
                db.initializeConn();
            }


        }

        private bool initialize4KCard(string name, DateTime DOB, char gender, short merchantID, int cardNum, short terminalID, string ID, string phone, string email, byte status )
        {
            byte[] blk0 = new byte[16]; byte[] blk1 = new byte[16]; byte[] blk2 = new byte[16]; byte[] data = new byte[16];
            byte[] shortArr = new byte[2]; byte[] intArr = new byte[4]; byte[] lngArr = new byte[8];
            int i = 0;
            Encoding ascii = Encoding.ASCII;
            Byte[] encodedBytes;

            try
            {
                //Block 0
                //Merchant ID
                shortArr = BitConverter.GetBytes(merchantID);
                blk0[0] = shortArr[0];
                blk0[1] = shortArr[1];

                //Initialization Time
                lngArr = BitConverter.GetBytes(DateTime.Now.ToUniversalTime().Ticks);
                blk0[2] = lngArr[0];
                blk0[3] = lngArr[1];
                blk0[4] = lngArr[2];
                blk0[5] = lngArr[3];
                blk0[6] = lngArr[4];
                blk0[7] = lngArr[5];
                blk0[8] = lngArr[6];
                blk0[9] = lngArr[7];

                //Card Number
                intArr = BitConverter.GetBytes(cardNum);
                blk0[10] = intArr[0];
                blk0[11] = intArr[1];
                blk0[12] = intArr[2];
                blk0[13] = intArr[3];

                //Terminal ID
                shortArr = BitConverter.GetBytes(terminalID);
                blk0[14] = shortArr[0];
                blk0[15] = shortArr[1];

                //Block 1 and 8 bytes of block 2
                //Name
                if (name.Length > 24) name = name.Substring(0, 24);
                name = name.PadRight(24, ' ');
                encodedBytes = ascii.GetBytes(name);
                for (i = 0; i < 16; i++)
                {
                    blk1[i] = encodedBytes[i];
                }
                for (i = 0; i < 8; i++)
                {
                    blk2[i] = encodedBytes[i + 16];
                }

                //Block 2 bytes 8-15
                //D.O.B
                shortArr = BitConverter.GetBytes(DOB.Year); //Year (4 digits)
                blk2[8] = shortArr[0];
                blk2[9] = shortArr[1];
                blk2[10] = Convert.ToByte(DOB.Month); //Month
                blk2[11] = Convert.ToByte(DOB.Day); //Day

                //Gender
                blk2[12] = Convert.ToByte(gender);

                //Life cycle +=1, status
                
                //if (CardComm.AuthBlock(255, KEYA))
                if (CardComm.AuthBlock(255, get_diversify_key(master, CardComm.currCardIDArr, 39)))
                {
                    if (CardComm.ReadBlock(242,data,0))
                    {
                        shortArr = BitConverter.GetBytes((BitConverter.ToInt16(data, 13)) + 1);
                        blk2[13] = shortArr[0];
                        blk2[14] = shortArr[1];
                        blk2[15] = status;
                    }
                    else
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCard] Error reading block 242"+ System.Environment.NewLine +": " + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error reading card data";
                        return false;
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[initialize4kCard] Error authenticating block 255"+ System.Environment.NewLine +": " + CardComm.lastErr + System.Environment.NewLine );
                    this.lastErr = "Authentication Error";
                    return false;
                }

                byte[] emptyBlk = new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

                //Write to Card Sector 39
                //if (CardComm.AuthBlock(255, KEYA))
                if (CardComm.AuthBlock(255, get_diversify_key(master, CardComm.currCardIDArr, 39)))
                {
                    if (!CardComm.WriteBlock(240, blk0, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCard] Error writing block 240"+ System.Environment.NewLine +": " + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!CardComm.WriteBlock(241, blk1, 0))
                    {

                        if (debug) Log.WriteLog("Error", "[initialize4kCard] Error writing block 241" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine );
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!CardComm.WriteBlock(242, blk2, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCard] Error writing block 242" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card  - " + CardComm.lastErr;
                        return false;
                    }

                    if (!CardComm.WriteBlock(243, emptyBlk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCard] Error writing block 243\n: " + CardComm.lastErr + "\n");
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!CardComm.WriteBlock(244, emptyBlk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCard] Error writing block 244\n: " + CardComm.lastErr + "\n");
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!CardComm.WriteBlock(245, emptyBlk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCard] Error writing block 245\n: " + CardComm.lastErr + "\n");
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!CardComm.WriteBlock(246, emptyBlk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCard] Error writing block 246\n: " + CardComm.lastErr + "\n");
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!CardComm.WriteBlock(247, emptyBlk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCard] Error writing block 247\n: " + CardComm.lastErr + "\n");
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!CardComm.WriteBlock(248, emptyBlk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCard] Error writing block 248\n: " + CardComm.lastErr + "\n");
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!CardComm.WriteBlock(249, emptyBlk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCard] Error writing block 249\n: " + CardComm.lastErr + "\n");
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!CardComm.WriteBlock(250, emptyBlk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCard] Error writing block 250\n: " + CardComm.lastErr + "\n");
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!CardComm.WriteBlock(251, emptyBlk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCard] Error writing block 251\n: " + CardComm.lastErr + "\n");
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    //Update database
                    try
                    {
                        if (debug)
                        {
                            Log.WriteLog("Database", "Executing SP: [dbo].[AddCard] '" + cardID + "'," + cardNum.ToString() + "," + merchantID.ToString() +
                                "," + terminalID.ToString() + ",'" + name + "','" + DOB.ToString("yyyy-MM-dd") + "','" + gender + "','" + ID + "'," + phone + "','" +
                                email + "'", "");
                        }

                        if (writeToDB)
                        {
                            db.OpenConnection();

                            //Register new card
                            SqlCommand cmd = db.MakeCommand_SP("[dbo].[AddCard]");
                            cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                            cmd.Parameters["@cardIDM"].Value = cardID;
                            cmd.Parameters.Add(new SqlParameter("@cardNum", SqlDbType.Int));
                            cmd.Parameters["@cardNum"].Value = cardNum;
                            cmd.Parameters.Add(new SqlParameter("@merchantID", SqlDbType.Int));
                            cmd.Parameters["@merchantID"].Value = merchantID;
                            cmd.Parameters.Add(new SqlParameter("@terminalID", SqlDbType.Int));
                            cmd.Parameters["@terminalID"].Value = terminalID;
                            cmd.Parameters.Add("@name", SqlDbType.VarChar);
                            cmd.Parameters["@name"].Value = name;
                            cmd.Parameters.Add(new SqlParameter("@dob", SqlDbType.SmallDateTime));
                            cmd.Parameters["@dob"].Value = DOB;
                            cmd.Parameters.Add(new SqlParameter("@gender", SqlDbType.Char));
                            cmd.Parameters["@gender"].Value = gender;
                            cmd.Parameters.Add(new SqlParameter("@holderID", SqlDbType.VarChar));
                            cmd.Parameters["@holderID"].Value = ID;
                            cmd.Parameters.Add(new SqlParameter("@phone", SqlDbType.VarChar));
                            cmd.Parameters["@phone"].Value = phone;
                            cmd.Parameters.Add(new SqlParameter("@email", SqlDbType.VarChar));
                            cmd.Parameters["@email"].Value = email;

                            cmd.ExecuteNonQuery();
                        }

                    }
                    catch (SqlException ex)
                    {
                        if (debug) Log.WriteLog("Exception", "SQL Exception in [initialize4kCard]", ex.Message + System.Environment.NewLine + ex.InnerException == null ? "" : ex.InnerException + System.Environment.NewLine);

                        lastErr = "[initializeCard] : Error updating database - " + ex.Message;
                        return false;
                    }
                    finally
                    {
                        if (writeToDB) db.CloseConnection();
                    }
                    return true;
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[initialize4kCard]: Authentication Error");

                    this.lastErr = "Authentication Error";
                    return false;
                }

            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[initialize4kCard]: SQL Exception", ex.Message + ex.InnerException == null ? "" : System.Environment.NewLine +ex.InnerException.Message);
                lastErr = "[initialize4kCard] :" + ex.Message  + ex.InnerException == null ? "" : " :" + ex.InnerException.Message;
                return false;
            }
        }

        private bool initialize1KCard(string name, DateTime DOB, char gender, short merchantID, int cardNum, short terminalID, string ID, string phone, string email, byte status)
        {
            byte[] blk0 = new byte[16]; byte[] blk1 = new byte[16]; byte[] blk2 = new byte[16]; byte[] data = new byte[16];
            byte[] shortArr = new byte[2]; byte[] intArr = new byte[4]; byte[] lngArr = new byte[8];
            int i = 0;
            Encoding ascii = Encoding.ASCII;
            Byte[] encodedBytes;

            try
            {
                //Block 0
                //Merchant ID
                shortArr = BitConverter.GetBytes(merchantID);
                blk0[0] = shortArr[0];
                blk0[1] = shortArr[1];

                //Initialization Time
                lngArr = BitConverter.GetBytes(DateTime.Now.ToUniversalTime().Ticks);
                blk0[2] = lngArr[0];
                blk0[3] = lngArr[1];
                blk0[4] = lngArr[2];
                blk0[5] = lngArr[3];
                blk0[6] = lngArr[4];
                blk0[7] = lngArr[5];
                blk0[8] = lngArr[6];
                blk0[9] = lngArr[7];

                //Card Number
                intArr = BitConverter.GetBytes(cardNum);
                blk0[10] = intArr[0];
                blk0[11] = intArr[1];
                blk0[12] = intArr[2];
                blk0[13] = intArr[3];

                //Terminal ID
                shortArr = BitConverter.GetBytes(terminalID);
                blk0[14] = shortArr[0];
                blk0[15] = shortArr[1];

                //Block 1 and 8 bytes of block 2
                //Name
                if (name.Length > 24) name = name.Substring(0, 24);
                name = name.PadRight(24, ' ');
                encodedBytes = ascii.GetBytes(name);
                for (i = 0; i < 16; i++)
                {
                    blk1[i] = encodedBytes[i];
                }
                for (i = 0; i < 8; i++)
                {
                    blk2[i] = encodedBytes[i + 16];
                }

                //Block 2 bytes 8-15
                //D.O.B
                shortArr = BitConverter.GetBytes(DOB.Year); //Year (4 digits)
                blk2[8] = shortArr[0];
                blk2[9] = shortArr[1];
                blk2[10] = Convert.ToByte(DOB.Month); //Month
                blk2[11] = Convert.ToByte(DOB.Day); //Day

                //Gender
                blk2[12] = Convert.ToByte(gender);

                //Life cycle +=1, status
                if (CardComm.AuthBlock(59, get_diversify_key(master,CardComm.currCardIDArr,14)))
                {
                    if (CardComm.ReadBlock(58, data, 0))
                    {
                        shortArr = BitConverter.GetBytes((BitConverter.ToInt16(data, 13)) + 1);
                        blk2[13] = shortArr[0];
                        blk2[14] = shortArr[1];
                        blk2[15] = status;
                    }
                    else
                    {
                        if (debug) Log.WriteLog("Error", "[initialize1kCard] Error reading block 58" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error reading card data";
                        return false;
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[initialize1kCard] Error authenticating block 59" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                    this.lastErr = "Authentication Error";
                    return false;
                }

                byte[] emptyBlk = new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

                //Write to Card Sector 14

                if (CardComm.AuthBlock(59, get_diversify_key(master,CardComm.currCardIDArr,14)))
                {
                    if (!CardComm.WriteBlock(56, blk0, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize1kCard] Error writing block 56" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!CardComm.WriteBlock(57, blk1, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize1kCard] Error writing block 57" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!CardComm.WriteBlock(58, blk2, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize1kCard] Error writing block 58" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card  - " + CardComm.lastErr;
                        return false;
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[initialize1kCard] Error authenticating block 59" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                    this.lastErr = "Authentication Error";
                    return false;
                }    

                //Write to card sector 15
                if (CardComm.AuthBlock(63, get_diversify_key(master,CardComm.currCardIDArr,15)))
                {
                    if (!CardComm.WriteBlock(60, emptyBlk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize1kCard] Error writing block 60" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!CardComm.WriteBlock(61, emptyBlk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize1kCard] Error writing block 61" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!CardComm.WriteBlock(62, emptyBlk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize1kCard] Error writing block 62" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    //Update database
                    try
                    {
                        if (debug)
                        {
                            Log.WriteLog("Database", "Executing SP: [dbo].[AddCard] '"+ cardID+"',"+cardNum.ToString()+","+merchantID.ToString()+
                                ","+terminalID.ToString()+",'"+name+"','"+DOB.ToString("yyyy-MM-dd")+"','"+gender+"','"+ID+"',"+phone+"','"+
                                email + "'", "" + System.Environment.NewLine);
                        }
                        if (writeToDB)
                        {
                            db.OpenConnection();

                            //Register new card
                            SqlCommand cmd = db.MakeCommand_SP("[dbo].[AddCard]");
                            cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                            cmd.Parameters["@cardIDM"].Value = cardID;
                            cmd.Parameters.Add(new SqlParameter("@cardNum", SqlDbType.Int));
                            cmd.Parameters["@cardNum"].Value = cardNum;
                            cmd.Parameters.Add(new SqlParameter("@merchantID", SqlDbType.Int));
                            cmd.Parameters["@merchantID"].Value = merchantID;
                            cmd.Parameters.Add(new SqlParameter("@terminalID", SqlDbType.Int));
                            cmd.Parameters["@terminalID"].Value = terminalID;
                            cmd.Parameters.Add("@name", SqlDbType.VarChar);
                            cmd.Parameters["@name"].Value = name;
                            cmd.Parameters.Add(new SqlParameter("@dob", SqlDbType.SmallDateTime));
                            cmd.Parameters["@dob"].Value = DOB;
                            cmd.Parameters.Add(new SqlParameter("@gender", SqlDbType.Char));
                            cmd.Parameters["@gender"].Value = gender;
                            cmd.Parameters.Add(new SqlParameter("@holderID", SqlDbType.VarChar));
                            cmd.Parameters["@holderID"].Value = ID;
                            cmd.Parameters.Add(new SqlParameter("@phone", SqlDbType.VarChar));
                            cmd.Parameters["@phone"].Value = phone;
                            cmd.Parameters.Add(new SqlParameter("@email", SqlDbType.VarChar));
                            cmd.Parameters["@email"].Value = email;

                            cmd.ExecuteNonQuery();
                        }
                    
                    }
                    catch (SqlException ex)
                    {
                        if (debug) Log.WriteLog("Exception", "SQL Exception in [initialize1kCard]", ex.Message + System.Environment.NewLine + ex.InnerException + System.Environment.NewLine);
                        lastErr = "[initialize1kCard] : Error updating database - " + ex.Message;
                        return false;
                    }
                    finally
                    {
                        if (writeToDB) db.CloseConnection();
                    }

                    return true;
                }
                
                else
                {
                    if (debug) Log.WriteLog("Error", "[initialize1kCard] Error authenticating block 63" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                    this.lastErr = "Authentication Error";
                    return false;
                }
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[initialize1kCard]: Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message + System.Environment.NewLine);
                lastErr = "[initialize1kCard] :" + ex.Message +  ex.InnerException== null ? "" : " :" +ex.InnerException.Message ;
                return false;
            }
        
        }

        /// <summary>
        /// Update card holder details
        /// </summary>
        /// <param name="name">Name of card holder</param>
        /// <param name="DOB">Date of Birth</param>
        /// <param name="gender">Gender - M: Male and F: Female</param>
        /// <param name="cardNum">Unique card number to identify this card</param>
        /// <param name="merchantID">Unique ID to identify the merchant</param>
        /// <param name="expiry">Expiry date of the card</param>
        /// <param name="ID">ID of the card holder</param>
        /// <param name="phone">Phone number of the card holder</param>
        /// <param name="email">Email address of the card holder</param>
        /// <param name="memID">Member ID of the card holder</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        public bool updateCard(string name, DateTime DOB, char gender, int cardNum, short merchantID, DateTime expiry, string ID, string phone, string email, string memID)
        {
            if (_cardType == MIFARE_1K) return update1kCard(name, DOB, gender, cardNum, merchantID, expiry,ID,phone,email, memID);
            else if (_cardType == MIFARE_4K) return update4kCard(name, DOB, gender, cardNum, merchantID, expiry, ID, phone, email, memID);
            else return false;
        }

        /// <summary>
        /// Update the status of the card
        /// </summary>
        /// <param name="status">The new status</param>
        /// <returns></returns>
        public bool updateCardStatus(byte status)
        {
            if (_cardType == MIFARE_1K) return update1kCardStatus(status);
            else if (_cardType == MIFARE_4K) return update4kCardStatus(status);
            else return false;
        }

        private bool update1kCardStatus(byte status)
        {
            byte[] blk = new byte[16];
            
            if (debug) Log.WriteLog("Invoke Method", "update1kCardStatus(" + status.ToString() +  ")" + System.Environment.NewLine);

            try
            {
                if (!readCard()) return false;
                if (CardComm.currCardID == String.Empty)
                {
                    if (debug) Log.WriteLog("Error", "[update1kCardStatus]: No Card Detected");
                    lastErr = "No Card Detected";
                    return false;
                }

                if (CardComm.AuthBlock(59, get_diversify_key(master, CardComm.currCardIDArr, 14)) == false)
                {
                    if (debug) Log.WriteLog("Error", "[update1kCardStatus]: Authentication Failed For Block 255" + System.Environment.NewLine + CardComm.lastErr);
                    lastErr = "Authentication Failed - " + CardComm.lastErr;
                    return false;
                }
                                 
                if (CardComm.ReadBlock(58, blk, 0))
                {
                    blk[15] = status;
                    if (!CardComm.WriteBlock(58, blk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[update1kCardStatus]: Error Writing to block 242" + System.Environment.NewLine + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[update1kCardStatus] Error reading block 58" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                    this.lastErr = "Error reading card data";
                    return false;
                }
                return true;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[update1kCardStatus]: Exception", ex.Message);
                lastErr = "[update1kCardStatus]: " + ex.Message;
                return false;
            }
        }

        private bool update4kCardStatus(byte status)
        {
            byte[] blk = new byte[16];

            if (debug) Log.WriteLog("Invoke Method", "update4kCardStatus(" + status.ToString() + ")" + System.Environment.NewLine);

            try
            {
                if (!readCard()) return false;
                if (CardComm.currCardID == String.Empty)
                {
                    if (debug) Log.WriteLog("Error", "[update4kCardStatus]: No Card Detected");
                    lastErr = "No Card Detected";
                    return false;
                }

                if (!CardComm.AuthBlock(255, get_diversify_key(master, CardComm.currCardIDArr, 39)))
                {
                    if (debug) Log.WriteLog("Error", "[update1kCardStatus]: Authentication Failed For Block 255" + System.Environment.NewLine + CardComm.lastErr);
                    lastErr = "Authentication Failed - " + CardComm.lastErr;
                    return false;
                }

                if (CardComm.ReadBlock(242, blk, 0))
                {
                    blk[15] = status;
                    if (!CardComm.WriteBlock(242, blk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[update4kCardStatus]: Error Writing to block 242" + System.Environment.NewLine + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[update4kCardStatus] Error reading block 58" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                    this.lastErr = "Error reading card data";
                    return false;
                }
                return true;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[update4kCardStatus]: Exception", ex.Message);
                lastErr = "[update4kCardStatus]: " + ex.Message;
                return false;
            }

        }
        
        private bool update1kCard(string name, DateTime DOB, char gender, int cardNum, short merchantID, DateTime expiry, string ID, string phone, string email, string memID)
        {
            byte[] blk0 = new byte[16]; byte[] blk1 = new byte[16]; byte[] blk2 = new byte[16]; byte[] blk3 = new byte[16];
            byte[] shortArr = new byte[2]; byte[] intArr = new byte[4]; byte[] lngArr = new byte[8];
            int i = 0;
            Encoding ascii = Encoding.ASCII;
            Byte[] encodedBytes;

            if (debug) Log.WriteLog("Invoke Method", "update1kCard(" + name + "," + DOB.ToString("yyyy-MM-dd") +
                    "," + gender + "," + Convert.ToString(cardNum) + "," + Convert.ToString(merchantID) + "," +
                    expiry.ToString("yyyy-MM-dd") + "," + Convert.ToString(ID) + "," + phone + "," + email + "," + memID + ")" + System.Environment.NewLine);

            try
            {
                if (!readCard()) return false;
                if (CardComm.currCardID == String.Empty)
                {
                    if (debug) Log.WriteLog("Error", "[update1kCard]: No Card Detected");
                    lastErr = "No Card Detected";
                    return false;
                }

                if (CardComm.AuthBlock(59, get_diversify_key(master,CardComm.currCardIDArr,14)) == false)
                {
                    if (debug) Log.WriteLog("Error", "[update1kCard]: Authentication Failed For Block 255" + System.Environment.NewLine + CardComm.lastErr);
                    lastErr = "Authentication Failed - " + CardComm.lastErr;
                    return false;
                }

                //Read Block 0: Issue Merchant, Issue Date, Card Number, Terminal ID
                if (CardComm.ReadBlock(56, blk0, 0))
                {
                    //Card Number
                    intArr = BitConverter.GetBytes(cardNum);
                    blk0[10] = intArr[0];
                    blk0[11] = intArr[1];
                    blk0[12] = intArr[2];
                    blk0[13] = intArr[3];

                    //Read Life Cycle
                    if (CardComm.ReadBlock(58, blk2, 0))
                    {                       
                        shortArr = BitConverter.GetBytes(BitConverter.ToInt16(blk2, 13));
                        blk2[13] = shortArr[0];
                        blk2[14] = shortArr[1];
                    }
                    else
                    {
                        if (debug) Log.WriteLog("Error", "[update1kCard] Error reading block 58" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error reading card data";
                        return false;
                    }

                    //Block 1 and 8 bytes of block 2
                    //Name
                    if (name.Length > 24) name = name.Substring(0, 24);
                    name = name.PadRight(24, ' ');
                    encodedBytes = ascii.GetBytes(name);
                    for (i = 0; i < 16; i++)
                    {
                        blk1[i] = encodedBytes[i];
                    }
                    for (i = 0; i < 8; i++)
                    {
                        blk2[i] = encodedBytes[i + 16];
                    }

                    //Block 2 bytes 8-15
                    //D.O.B
                    shortArr = BitConverter.GetBytes(DOB.Year); //Year (4 digits)
                    blk2[8] = shortArr[0];
                    blk2[9] = shortArr[1];
                    blk2[10] = Convert.ToByte(DOB.Month); //Month
                    blk2[11] = Convert.ToByte(DOB.Day); //Day

                    //Gender
                    blk2[12] = Convert.ToByte(gender);
                    
                    if (!CardComm.WriteBlock(56, blk0, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[update1kCard]: Error Writing to block 240" + System.Environment.NewLine + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!CardComm.WriteBlock(57, blk1, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[update1kCard]: Error Writing to block 241" + System.Environment.NewLine + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!CardComm.WriteBlock(58, blk2, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[update1kCard]: Error Writing to block 242" + System.Environment.NewLine + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    //Update purse expiry and member ID
                    short sector = getProgramSector(merchantID, 4);
                    if (!editEPurse(Convert.ToInt16(sector), 0, expiry, merchantID, memID))
                        return false;

                    //update loyalty points expiry and member ID
                    short order = 0;
                    foreach (KeyValuePair<DirData, short> item in merchantDict)
                    {
                        if (item.Key.programType >> 4 == 2)
                        {
                            order = Convert.ToInt16((item.Key.programType & 15));
                            sector = getProgramSector(merchantID, 2, order);
                            if (!editLoyalty(Convert.ToInt16(sector),order, 0, expiry, merchantID, memID))
                                return false;
                        }
                    }
                    //sector = getProgramSector(merchantID, 2);
                    //if (!editLoyalty(Convert.ToInt16(sector), 0, expiry, merchantID, memID))
                    //    return false;

                    //update database
                    try
                    {
                        if (debug)
                        {
                            Log.WriteLog("Database", "Executing SP: [dbo].[UpdateCard] '" + cardID + "'," + cardNum.ToString() + "," + merchantID.ToString() +
                                ",'" + name + "','" + DOB.ToString("yyyy-MM-dd") + "','" + gender + "','" + ID + "'," + phone + "','" +
                                email + "','"+ memID + "'", "");
                        }

                        if (writeToDB)
                        {

                            db.OpenConnection();
                            SqlCommand cmd = db.MakeCommand_SP("[dbo].[UpdateCard]");
                            cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                            cmd.Parameters["@cardIDM"].Value = cardID;
                            cmd.Parameters.Add(new SqlParameter("@cardNum", SqlDbType.Int));
                            cmd.Parameters["@cardNum"].Value = cardNum;
                            cmd.Parameters.Add(new SqlParameter("@merchantID", SqlDbType.Int));
                            cmd.Parameters["@merchantID"].Value = merchantID;
                            cmd.Parameters.Add(new SqlParameter("@name", SqlDbType.VarChar));
                            cmd.Parameters["@name"].Value = name;
                            cmd.Parameters.Add(new SqlParameter("@DOB", SqlDbType.SmallDateTime));
                            cmd.Parameters["@DOB"].Value = DOB;
                            cmd.Parameters.Add(new SqlParameter("@gender", SqlDbType.Char));
                            cmd.Parameters["@gender"].Value = gender;
                            cmd.Parameters.Add(new SqlParameter("@holderID", SqlDbType.Char));
                            cmd.Parameters["@holderID"].Value = ID;
                            cmd.Parameters.Add(new SqlParameter("@phone", SqlDbType.Char));
                            cmd.Parameters["@phone"].Value = phone;
                            cmd.Parameters.Add(new SqlParameter("@email", SqlDbType.Char));
                            cmd.Parameters["@email"].Value = email;
                            cmd.Parameters.Add(new SqlParameter("@memID", SqlDbType.Char));
                            cmd.Parameters["@memID"].Value = memID;

                            cmd.ExecuteNonQuery();
                        }
                        return true;
                    }
                    catch (SqlException ex)
                    {
                        if (debug) Log.WriteLog("Exception", "[update1kCard]:SQL Exception", ex.Message + (ex.InnerException == null ? "" : System.Environment.NewLine + ex.InnerException.Message));
                        lastErr = "[update1kCard] : Error updating database - " + ex.Message;
                        return false;
                    }
                    finally
                    {
                        if (writeToDB) db.CloseConnection();
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[update1kCard]: Error Reading Block 56" + System.Environment.NewLine + CardComm.lastErr + System.Environment.NewLine);
                    lastErr = "Error Reading Card - " + CardComm.lastErr;
                    return false;
                }
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[update1kCard]: Exception", ex.Message + (ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message));
                lastErr = "[updateCard]: " + ex.Message + (ex.InnerException == null ? "" : ":" + ex.InnerException.Message);
                return false;
            }

        }

        private bool update4kCard(string name, DateTime DOB, char gender, int cardNum, short merchantID, DateTime expiry, string ID, string phone, string email, string memID)
        {
            byte[] blk0 = new byte[16]; byte[] blk1 = new byte[16]; byte[] blk2 = new byte[16]; byte[] blk3 = new byte[16];
            byte[] shortArr = new byte[2]; byte[] intArr = new byte[4]; byte[] lngArr = new byte[8];
            int i = 0;
            Encoding ascii = Encoding.ASCII;
            Byte[] encodedBytes;

            if (debug) Log.WriteLog("Invoke Method", "update4kCard(" + name + "," + DOB.ToString("yyyy-MM-dd") +
                    "," + gender + "," + Convert.ToString(cardNum) + "," + Convert.ToString(merchantID) + "," +
                    expiry.ToString("yyyy-MM-dd") + "," + Convert.ToString(ID) + "," + phone + "," + email + "," + memID +")" + System.Environment.NewLine);

            try
            {
                if (CardComm.currCardID == String.Empty)
                {
                    if (debug) Log.WriteLog("Error", "[update4kCard]: No Card Detected");
                    lastErr = "No Card Detected";
                    return false;
                }

                //if (CardComm.AuthBlock(255, KEYA))
                if (!CardComm.AuthBlock(255, get_diversify_key(master, CardComm.currCardIDArr, 39)))
                {
                    if (debug) Log.WriteLog("Error", "[update4kCard]: Authentication Failed For Block 255" + System.Environment.NewLine + CardComm.lastErr);
                    lastErr = "Authentication Failed - " + CardComm.lastErr;
                    return false;
                }

                //Read Block 0: Issue Merchant, Issue Date, Card Number, Terminal ID
                if (CardComm.ReadBlock(240, blk0, 0))
                {
                    //Card Number
                    intArr = BitConverter.GetBytes(cardNum);
                    blk0[10] = intArr[0];
                    blk0[11] = intArr[1];
                    blk0[12] = intArr[2];
                    blk0[13] = intArr[3];

                    //Life cycle
                    if (CardComm.ReadBlock(242, blk2, 0))
                    {
                        shortArr = BitConverter.GetBytes(BitConverter.ToInt16(blk2, 13));
                        blk2[13] = shortArr[0];
                        blk2[14] = shortArr[1];
                    }
                    else
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCard] Error reading block 242" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error reading card data";
                        return false;
                    }

                    //Block 1 and 8 bytes of block 2
                    //Name
                    if (name.Length > 24) name = name.Substring(0, 24);
                    name = name.PadRight(24, ' ');
                    encodedBytes = ascii.GetBytes(name);
                    for (i = 0; i < 16; i++)
                    {
                        blk1[i] = encodedBytes[i];
                    }
                    for (i = 0; i < 8; i++)
                    {
                        blk2[i] = encodedBytes[i + 16];
                    }

                    //Block 2 bytes 8-15
                    //D.O.B
                    shortArr = BitConverter.GetBytes(DOB.Year); //Year (4 digits)
                    blk2[8] = shortArr[0];
                    blk2[9] = shortArr[1];
                    blk2[10] = Convert.ToByte(DOB.Month); //Month
                    blk2[11] = Convert.ToByte(DOB.Day); //Day

                    //Gender
                    blk2[12] = Convert.ToByte(gender);

                    if (!CardComm.WriteBlock(240, blk0, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[update4kCard]: Error Writing to block 240" + System.Environment.NewLine + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!CardComm.WriteBlock(241, blk1, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[update4kCard]: Error Writing to block 241" + System.Environment.NewLine + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!CardComm.WriteBlock(242, blk2, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[update4kCard]: Error Writing to block 242" + System.Environment.NewLine + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }
                    
                    //Update purse expiry and member ID
                    short sector = getProgramSector(merchantID, 4);
                    if (!editEPurse(Convert.ToInt16(sector), 0, expiry, merchantID, memID))
                        return false;

                    //update loyalty points expiry and member ID
                    short order = 0;
                    foreach (KeyValuePair<DirData, short> item in merchantDict)
                    {
                        if (item.Value >> 4 == 2)
                        {
                            order = Convert.ToInt16((item.Value & 15));
                            if (!editLoyalty(Convert.ToInt16(sector), order, 0, expiry, merchantID, memID))
                                return false;
                        }
                    }
                    //sector = getProgramSector(merchantID, 2);
                    //if (!editLoyalty(Convert.ToInt16(sector), 0, expiry, merchantID, memID))
                    //    return false;

                    //update database
                    try
                    {
                        if (debug)
                        {
                            Log.WriteLog("Database", "Executing SP: [dbo].[UpdateCard] '" + cardID + "'," + cardNum.ToString() + "," + merchantID.ToString() +
                                ",'" + name + "','" + DOB.ToString("yyyy-MM-dd") + "','" + gender + "','" + ID + "'," + phone + "','" +
                                email + "','"+ memID + "'", "");
                        }

                        if (writeToDB)
                        {

                            db.OpenConnection();
                            SqlCommand cmd = db.MakeCommand_SP("[dbo].[UpdateCard]");
                            cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                            cmd.Parameters["@cardIDM"].Value = cardID;
                            cmd.Parameters.Add(new SqlParameter("@cardNum", SqlDbType.Int));
                            cmd.Parameters["@cardNum"].Value = cardNum;
                            cmd.Parameters.Add(new SqlParameter("@merchantID", SqlDbType.Int));
                            cmd.Parameters["@merchantID"].Value = merchantID;
                            cmd.Parameters.Add(new SqlParameter("@name", SqlDbType.VarChar));
                            cmd.Parameters["@name"].Value = name;
                            cmd.Parameters.Add(new SqlParameter("@DOB", SqlDbType.SmallDateTime));
                            cmd.Parameters["@DOB"].Value = DOB;
                            cmd.Parameters.Add(new SqlParameter("@gender", SqlDbType.Char));
                            cmd.Parameters["@gender"].Value = gender;
                            cmd.Parameters.Add(new SqlParameter("@holderID", SqlDbType.Char));
                            cmd.Parameters["@holderID"].Value = ID;
                            cmd.Parameters.Add(new SqlParameter("@phone", SqlDbType.Char));
                            cmd.Parameters["@phone"].Value = phone;
                            cmd.Parameters.Add(new SqlParameter("@email", SqlDbType.Char));
                            cmd.Parameters["@email"].Value = email;
                            cmd.Parameters.Add(new SqlParameter("@memID", SqlDbType.Char));
                            cmd.Parameters["@memID"].Value = email;

                            cmd.ExecuteNonQuery();
                        }
                        return true;
                    }
                    catch (SqlException ex)
                    {
                        if (debug) Log.WriteLog("Exception", "[update4kCard]:SQL Exception", ex.Message + (ex.InnerException == null ? "" : System.Environment.NewLine + ex.InnerException.Message));
                        lastErr = "[update4kCard] : Error updating database - " + ex.Message;
                        return false;
                    }
                    finally
                    {
                        if (writeToDB) db.CloseConnection();
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[update4kCard]: Error Reading Block 240" + System.Environment.NewLine + CardComm.lastErr + System.Environment.NewLine);
                    lastErr = "Error Reading Card - " + CardComm.lastErr;
                    return false;
                }
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[update4kCard]: Exception", ex.Message + (ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message));
                lastErr = "[update4kCard]: " + ex.Message + (ex.InnerException == null ? "" : ":" + ex.InnerException.Message);
                return false;
            }

        }

        /// <summary>
        /// Reads basic card details (Name, D.O.B, Expiry Date, Card Number, Gender) only
        /// </summary>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        private bool readCard()
        {
            if (_cardType == MIFARE_1K)
            {
                CardComm.DisconnectCard();
                return read1KCard();
            }
            else if (_cardType == MIFARE_4K)
            {
                CardComm.DisconnectCard();
                return read4kCard();
            }
            else return false;
        }

        private bool read4kCard()
        {
            //Read info from block 39
            byte[] data = new byte[16];

            try
            {
                if (debug) Log.WriteLog("Invoke Method", "read4kCard()");
                if (CardComm.currCardID == String.Empty)
                {
                    if (debug) Log.WriteLog("Error", "[read4kCard]: No Card Detected");
                    lastErr = "No Card Detected!";
                    return false;
                }

                //if (CardComm.AuthBlock(255, KEYA))
                if (!CardComm.AuthBlock(255, get_diversify_key(master, CardComm.currCardIDArr, 39)))
                {
                    if (debug) Log.WriteLog("Error", "[read4kCard]: Authentication Failed for Block 255");
                    lastErr = "Authentication Failed  - " + CardComm.lastErr;
                    return false;
                }

                //Read Block 0: Issue Merchant, Issue Date, Card Number, Terminal ID
                if (CardComm.ReadBlock(240, data, 0))
                {
                    _issueMerchantID = BitConverter.ToInt16(data, 0);
                    _issueDateTime = new DateTime(BitConverter.ToInt64(data, 2), DateTimeKind.Utc).ToLocalTime();
                    _issueCardID = BitConverter.ToInt32(data, 10);
                    _issueTerminalID = BitConverter.ToInt16(data, 14);
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read4kCard]: Error reading block 240" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read Block 1: Name 1
                if (CardComm.ReadBlock(241, data, 0))
                {
                    _cardHolderName = ASCIIEncoding.ASCII.GetString(data, 0, 16);
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read4kCard]: Error reading block 241" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read Block 2: Name 2, D.O.B, Gender, Status
                if (CardComm.ReadBlock(242, data, 0))
                {
                    _cardHolderName = _cardHolderName + ASCIIEncoding.ASCII.GetString(data, 0, 8);
                    short year = BitConverter.ToInt16(data, 8);
                    byte mth = data[10];
                    byte day = data[11];
                    if (year == 0 || mth == 0 || year == 0)
                    {
                        _cardHolderDOB = new DateTime(1, 1, 1);
                    }
                    else _cardHolderDOB = new DateTime(BitConverter.ToInt16(data, 8), data[10], data[11]).Date;
                    _cardHolderGender = Convert.ToChar(ASCIIEncoding.ASCII.GetString(data, 12, 1));
                    _cardStatus = data[15];
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read4kCard]: Error reading block 242" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                _cardHolderName = _cardHolderName.Trim();


                //Read Block 3: Sectors Used
                if (CardComm.ReadBlock(243, data, 0))
                {
                    sectorsUsed = BitConverter.ToInt64(data, 0);
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read4kCard]: Error reading block 243" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read Block 4: Sector 1-5 info
                if (CardComm.ReadBlock(244, data, 0))
                {
                    DirData currDir;
                    for (int i = 0; i < 4; i++)
                    {
                        currDir.merchantID = BitConverter.ToInt16(data, i * 3);
                        currDir.programType = data[i * 3 + 2];
                        if (currDir.merchantID != 0 && merchantDict.ContainsKey(currDir) == false) merchantDict.Add(currDir, Convert.ToInt16(i));
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read4kCard]: Error reading block 244" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read Block 5: Sector 6-10 info
                if (CardComm.ReadBlock(245, data, 0))
                {
                    DirData currDir;
                    for (int i = 0; i < 5; i++)
                    {
                        currDir.merchantID = BitConverter.ToInt16(data, i * 3);
                        currDir.programType = data[i * 3 + 2];
                        if (currDir.merchantID != 0 && merchantDict.ContainsKey(currDir) == false) merchantDict.Add(currDir, Convert.ToInt16(i + 6));
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read4kCard]: Error reading block 245" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read Block 6: Sector 11-15 info
                if (CardComm.ReadBlock(246, data, 0))
                {
                    DirData currDir;
                    for (int i = 0; i < 5; i++)
                    {
                        currDir.merchantID = BitConverter.ToInt16(data, i * 3);
                        currDir.programType = data[i * 3 + 2];
                        if (currDir.merchantID != 0 && merchantDict.ContainsKey(currDir) == false) merchantDict.Add(currDir, Convert.ToInt16(i + 11));
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read4kCard]: Error reading block 246" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read Block 7: Sector 16-20 info
                if (CardComm.ReadBlock(247, data, 0))
                {
                    DirData currDir;
                    for (int i = 0; i < 5; i++)
                    {
                        currDir.merchantID = BitConverter.ToInt16(data, i * 3);
                        currDir.programType = data[i * 3 + 2];
                        if (currDir.merchantID != 0 && merchantDict.ContainsKey(currDir) == false) merchantDict.Add(currDir, Convert.ToInt16(i + 16));
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read4kCard]: Error reading block 247" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read Block 8: Sector 21-25 info
                if (CardComm.ReadBlock(248, data, 0))
                {
                    DirData currDir;
                    for (int i = 0; i < 5; i++)
                    {
                        currDir.merchantID = BitConverter.ToInt16(data, i * 3);
                        currDir.programType = data[i * 3 + 2];
                        if (currDir.merchantID != 0 && merchantDict.ContainsKey(currDir) == false) merchantDict.Add(currDir, Convert.ToInt16(i + 21));
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read4kCard]: Error reading block 248" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read Block 9: Sector 26-30 info
                if (CardComm.ReadBlock(249, data, 0))
                {
                    DirData currDir;
                    for (int i = 0; i < 5; i++)
                    {
                        currDir.merchantID = BitConverter.ToInt16(data, i * 3);
                        currDir.programType = data[i * 3 + 2];
                        if (currDir.merchantID != 0 && merchantDict.ContainsKey(currDir) == false) merchantDict.Add(currDir, Convert.ToInt16(i + 26));
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read4kCard]: Error reading block 249" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read Block 10: Sector 31-35 info
                if (CardComm.ReadBlock(250, data, 0))
                {
                    DirData currDir;
                    for (int i = 0; i < 5; i++)
                    {
                        currDir.merchantID = BitConverter.ToInt16(data, i * 3);
                        currDir.programType = data[i * 3 + 2];
                        if (currDir.merchantID != 0 && merchantDict.ContainsKey(currDir) == false) merchantDict.Add(currDir, Convert.ToInt16(i + 31));
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read4kCard]: Error reading block 250" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read from database
                try
                {
                    if (debug)
                    {
                        Log.WriteLog("Database", "Executing SP: [dbo].[GetCardHolderInfo] '" + cardID + "'");
                    }
                    if (writeToDB)
                    {
                        DataSet ds = new DataSet();
                        SqlDataAdapter da = new SqlDataAdapter();
                        SqlCommand cmd = db.MakeCommand_SP("dbo.GetCardHolderInfo");
                        cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                        cmd.Parameters["@cardIDM"].Value = cardID;
                        da.SelectCommand = cmd;
                        da.Fill(ds);

                        _cardHolderID = ds.Tables[0].Rows[0]["HolderID"].ToString();
                        _cardHolderPhone = ds.Tables[0].Rows[0]["Phone"].ToString();
                        _cardHolderEmail = ds.Tables[0].Rows[0]["Email"].ToString();

                        da.Dispose();
                        ds.Dispose();
                    }

                }
                catch (SqlException ex)
                {
                    if (debug) Log.WriteLog("Exception", "[read4kCard]:SQL Exception", ex.Message + "" + System.Environment.NewLine + ex.InnerException);

                    lastErr = "[read4kCard] : Error reading from database - " + ex.Message;
                    return false;
                }
                finally
                {
                    if (writeToDB) db.CloseConnection();
                }

                return true;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[read4kCard]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[read4kCard] " + ex.Message + ex.InnerException == null ? "" : ":"  + ex.InnerException.Message;
                return false;
            }
        }

        private bool read1KCard()
        {
            //Read info from sector 14 & 15
            byte[] data = new byte[16];

            try
            {
                if (debug) Log.WriteLog("Invoke Method", "read1kCard()");
                if (CardComm.currCardID == String.Empty)
                {
                    if (debug) Log.WriteLog("Error", "[read1kCard]: No Card Detected");
                    lastErr = "No Card Detected!";
                    return false;
                }

                //Authenticate sector 14
                if (CardComm.AuthBlock(59, get_diversify_key(master,CardComm.currCardIDArr,14)) == false)
                {
                    if (debug) Log.WriteLog("Error", "[read1kCard]: Authentication Failed for Block 59");
                    lastErr = "Authentication Failed  - " + CardComm.lastErr;
                    return false;
                }

                //Read Block 0: Issue Merchant, Issue Date, Card Number, Terminal ID
                if (CardComm.ReadBlock(56, data, 0))
                {
                    _issueMerchantID = BitConverter.ToInt16(data, 0);
                    _issueDateTime = new DateTime(BitConverter.ToInt64(data, 2), DateTimeKind.Utc).ToLocalTime();
                    _issueCardID = BitConverter.ToInt32(data, 10);
                    _issueTerminalID = BitConverter.ToInt16(data, 14);
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read1kCard]: Error reading block 56"+ System.Environment.NewLine  + CardComm.lastErr);
                    return false;
                }

                //Read Block 1: Name 1
                if (CardComm.ReadBlock(57, data, 0))
                {
                    _cardHolderName = ASCIIEncoding.ASCII.GetString(data, 0, 16);
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read1kCard]: Error reading block 57" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read Block 2: Name 2 (1st 8 bytes),D.O.B, Gender
                if (CardComm.ReadBlock(58, data, 0))
                {
                    _cardHolderName = _cardHolderName + ASCIIEncoding.ASCII.GetString(data, 0, 8);
                    short year = BitConverter.ToInt16(data, 8);
                    byte mth = data[10];
                    byte day = data[11];
                    if (year == 0 || mth == 0 || day == 0)
                        _cardHolderDOB = new DateTime(1, 1, 1);
                    else
                        _cardHolderDOB = new DateTime(BitConverter.ToInt16(data, 8), data[10], data[11]).Date;
                    _cardHolderGender = Convert.ToChar(ASCIIEncoding.ASCII.GetString(data, 12, 1));
                    _cardStatus = data[15];
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read1kCard]: Error reading block 58" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                _cardHolderName = _cardHolderName.Trim();
                

                //Authenticate sector 15
                if (CardComm.AuthBlock(63, get_diversify_key(master,CardComm.currCardIDArr,15)) == false)
                {
                    if (debug) Log.WriteLog("Error", "[read1kCard]: Authentication Failed for Block 63");
                    lastErr = "Authentication Failed  - " + CardComm.lastErr;
                    return false;
                }

                //Read Block 1: Sectors Used
                if (CardComm.ReadBlock(60, data, 0))
                {
                    sectorsUsed = BitConverter.ToInt64(data, 0);
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read1kCard]: Error reading block 60" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read Block 2: Sector 1-5 info
                if (CardComm.ReadBlock(61, data, 0))
                {
                    DirData currDir;
                    for (int i = 0; i < 5; i++)
                    {
                        currDir.merchantID = BitConverter.ToInt16(data, i * 3);
                        currDir.programType = data[i * 3 + 2];
                        if (currDir.merchantID != 0 && merchantDict.ContainsKey(currDir) == false) merchantDict.Add(currDir, Convert.ToInt16(i));
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read1kCard]: Error reading block 61" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read Block 3: Sector 6-10 info
                if (CardComm.ReadBlock(62, data, 0))
                {
                    DirData currDir;
                    for (int i = 0; i < 5; i++)
                    {
                        currDir.merchantID = BitConverter.ToInt16(data, i * 3);
                        currDir.programType = data[i * 3 + 2];
                        if (currDir.merchantID != 0 && merchantDict.ContainsKey(currDir) == false) merchantDict.Add(currDir, Convert.ToInt16(i + 5));
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read1kCard]: Error reading block 62" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read from database
                try
                {
                    if (debug)
                    {
                        Log.WriteLog("Database", "Executing SP: [dbo].[GetCardHolderInfo] '" + cardID + "'");
                    }
                    if (writeToDB)
                    {
                        DataSet ds = new DataSet();
                        SqlDataAdapter da = new SqlDataAdapter();
                        SqlCommand cmd = db.MakeCommand_SP("dbo.GetCardHolderInfo");
                        cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                        cmd.Parameters["@cardIDM"].Value = cardID;
                        da.SelectCommand = cmd;
                        da.Fill(ds);

                        _cardHolderID = ds.Tables[0].Rows[0]["HolderID"].ToString();
                        _cardHolderPhone = ds.Tables[0].Rows[0]["Phone"].ToString();
                        _cardHolderEmail = ds.Tables[0].Rows[0]["Email"].ToString();

                        da.Dispose();
                        ds.Dispose();
                    }

                }
                catch (SqlException ex)
                {
                    if (debug) Log.WriteLog("Exception", "[read1kCard]:SQL Exception", ex.Message + "" + System.Environment.NewLine + ex.InnerException);

                    lastErr = "[read4kCard] : Error reading from database - " + ex.Message;
                    return false;
                }
                finally
                {
                    if (writeToDB) db.CloseConnection();
                }
                return true;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[read1kCard]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[read1kCard] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }

        /// <summary>
        /// Deletes a program stored in the specified sector of the card
        /// </summary>
        /// <param name="sector">Sector in which the program to be deleted is stored</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        private bool delProgram(short sector)
        {
            try
            {
                byte[] blank = new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
                for (int i = 0; i < 3; i++)
                {
                    if (!CardComm.WriteBlock(sector * 4 + i, blank, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[delProgram]: Error writing block " + Convert.ToString((sector * 4 + i)) + System.Environment.NewLine + CardComm.lastErr);
                        return false;
                    }
                }
                //Clear Directory Record
                return updateDirectory(sector, 0, 0);
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[delProgram]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[delProgram] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }

        /// <summary>
        /// Updates directory information of the card when a program is added/deleted
        /// </summary>
        /// <param name="sector">The sector in which the program is stored</param>
        /// <param name="merchant">The merchant who owns the program</param>
        /// <param name="programType">The program type</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        private bool updateDirectory(short sector, short merchant, byte programType)
        {
            if (_cardType == MIFARE_1K) return update1kDirectory(sector, merchant, programType);
            else if (_cardType == MIFARE_4K) return update4kDirectory(sector,merchant,programType);
            else return false;
        }

        private bool update1kDirectory(short sector, short merchant, byte programType)
        {
            try
            {
                short blkNum = Convert.ToInt16(sector / 5);
                //if (sector % 5 != 0) blkNum++;
                blkNum = Convert.ToInt16(blkNum + 61);

                short byteNum = sector;
                if (byteNum >= 5) byteNum = Convert.ToInt16(byteNum % 5);
                //if (byteNum == 0) byteNum = Convert.ToInt16(sector/5);
                byteNum = Convert.ToInt16((byteNum) * 3);

                //Read original block data
                byte[] data = new byte[16];

                if (CardComm.currCardID == String.Empty)
                {
                    if (debug) Log.WriteLog("Error", "[update1kDirectory]: No Card Detected");
                    lastErr = "No Card Detected!";
                    return false;
                }

                if (CardComm.AuthBlock(63, get_diversify_key(master,CardComm.currCardIDArr,15)) == false)
                {
                    if (debug) Log.WriteLog("Error", "[update1kDirectory]: Authentication Failed for Block 63");
                    lastErr = "Authentication Failed!";
                    return false;
                }

                if (!CardComm.ReadBlock(blkNum, data, 0))
                {
                    if (debug) Log.WriteLog("Error", "[update1kDirectory]: Error Reading Block " + Convert.ToString(blkNum));
                    lastErr = "Error Reading Card";
                    return false;
                }

                byte[] shortArr = new byte[2];
                shortArr = BitConverter.GetBytes(merchant);
                data[byteNum] = shortArr[0];
                data[byteNum + 1] = shortArr[1];
                data[byteNum + 2] = programType;

                if (!CardComm.WriteBlock(blkNum, data, 0))
                {
                    lastErr = "Error Updating Directory Info!";
                    return false;
                }

                //Update sectors used 
                string sect = Convert.ToString(sectorsUsed, 2);
                sect = sect.PadLeft(64, '0');
                char[] charArr = sect.ToCharArray();
                Array.Reverse(charArr);
                sect = new string(charArr);
                sect = sect.Remove(sector, 1);
                if (merchant == 0)
                    sect = sect.Insert(sector, "0");
                else
                    sect = sect.Insert(sector, "1");

                charArr = sect.ToCharArray();
                Array.Reverse(charArr);
                long sectLng = Convert.ToInt64(new string(charArr), 2);
                if (!CardComm.ReadBlock(60, data, 0))
                {
                    if (debug) Log.WriteLog("Error", "[update1kDirectory]: Error Writing Block " + Convert.ToString(blkNum));
                    lastErr = "Error Reading Card";
                    return false;
                }

                byte[] lngArr = new byte[8];
                lngArr = BitConverter.GetBytes(sectLng);
                for (int i = 0; i < 8; i++)
                {
                    data[i] = lngArr[i];
                }

                if (!CardComm.WriteBlock(60, data, 0))
                {
                    if (debug) Log.WriteLog("Error", "[update1kDirectory]: Error Reading Block 60");
                    lastErr = "Error Updating Directory Info!";
                    return false;
                }

                DirData currDir = new DirData();
                currDir.merchantID = merchant;
                currDir.programType = programType;
                if (currDir.merchantID != 0 && merchantDict.ContainsKey(currDir) == false) merchantDict.Add(currDir, Convert.ToInt16(sector));
                return true;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[update1kDirectory]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[update1kDirectory] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }

        private bool update4kDirectory(short sector, short merchant, byte programType)
        {
            try
            {
                short blkNum = Convert.ToInt16(sector / 5);
                if (sector % 5 != 0) blkNum++;
                blkNum = Convert.ToInt16(blkNum + 243);

                short byteNum = sector;
                if (byteNum > 5) byteNum = Convert.ToInt16(byteNum % 5);
                if (byteNum == 0) byteNum = Convert.ToInt16(sector / 5);
                byteNum = Convert.ToInt16((byteNum - 1) * 3);

                //Read original block data
                byte[] data = new byte[16];

                if (CardComm.currCardID == String.Empty)
                {
                    if (debug) Log.WriteLog("Error", "[update4kDirectory]: No Card Detected");
                    lastErr = "No Card Detected!";
                    return false;
                }

                //if (CardComm.AuthBlock(255, KEYA))
                if (!CardComm.AuthBlock(255, get_diversify_key(master, CardComm.currCardIDArr, 39)))
                {
                    if (debug) Log.WriteLog("Error", "[update4kDirectory]: Authentication Failed for Block 255");
                    lastErr = "Authentication Failed!";
                    return false;
                }

                if (!CardComm.ReadBlock(blkNum, data, 0))
                {
                    if (debug) Log.WriteLog("Error", "[update4kDirectory]: Error Reading Block " + Convert.ToString(blkNum));
                    lastErr = "Error Reading Card";
                    return false;
                }

                byte[] shortArr = new byte[2];
                shortArr = BitConverter.GetBytes(merchant);
                data[byteNum] = shortArr[0];
                data[byteNum + 1] = shortArr[1];
                data[byteNum + 2] = programType;

                if (!CardComm.WriteBlock(blkNum, data, 0))
                {
                    if (debug) Log.WriteLog("Error", "[update4kDirectory]: Error Writing Block " + Convert.ToString(blkNum));
                    lastErr = "Error Updating Directory Info!";
                    return false;
                }

                //Update sectors used 
                string sect = Convert.ToString(sectorsUsed, 2);
                sect = sect.PadLeft(64, '0');
                char[] charArr = sect.ToCharArray();
                Array.Reverse(charArr);
                sect = new string(charArr);
                sect = sect.Remove(sector, 1);
                if (merchant == 0)
                    sect = sect.Insert(sector, "0");
                else
                    sect = sect.Insert(sector, "1");

                charArr = sect.ToCharArray();
                Array.Reverse(charArr);
                long sectLng = Convert.ToInt64(new string(charArr), 2);
                if (!CardComm.ReadBlock(243, data, 0))
                {
                    if (debug) Log.WriteLog("Error", "[update4kDirectory]: Error Reading Block 243");
                    lastErr = "Error Reading Card";
                    return false;
                }

                byte[] lngArr = new byte[8];
                lngArr = BitConverter.GetBytes(sectLng);
                for (int i = 0; i < 8; i++)
                {
                    data[i] = lngArr[i];
                }

                if (!CardComm.WriteBlock(243, data, 0))
                {
                    if (debug) Log.WriteLog("Error", "[update4kDirectory]: Error Writing Block 243");
                    lastErr = "Error Updating Directory Info!";
                    return false;
                }

                DirData currDir = new DirData();
                currDir.merchantID = merchant;
                currDir.programType = programType;
                if (currDir.merchantID != 0 && merchantDict.ContainsKey(currDir) == false) merchantDict.Add(currDir, Convert.ToInt16(sector));
                return true;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[update4kDirectory]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[update4kDirectory] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }

        /// <summary>
        /// Returns the sector where the program information is stored
        /// </summary>
        /// <param name="merchant">Merchant ID of the merchant which owns the program</param>
        /// <param name="progType">Program Type</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        private short getProgramSector(short merchant, short progType)
        {
            try
            {
                DirData dir = new DirData();
                dir.merchantID = merchant;

                if (progType == 2)
                    dir.programType = BitConverter.GetBytes((progType << 4) & 1)[0];
                else dir.programType = progType;

                if (merchantDict.ContainsKey(dir))
                {
                    return merchantDict[dir];
                }
                else return 0;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[getProgramSector]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[getProgramSector] " + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return 0;
            }

        }

        /// <summary>
        /// Returns the sector where the program information is stored
        /// </summary>
        /// <param name="merchant">Merchant ID of the merchant which owns the program</param>
        /// <param name="progType">Program Type</param>
        /// <param name="order">The order of the program</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        private short getProgramSector(short merchant, short progType, short order)
        {
            try
            {
                DirData dir = new DirData();
                dir.merchantID = merchant;
                dir.programType = BitConverter.GetBytes(Convert.ToByte(progType << 4) | Convert.ToByte(order))[0];
                if (merchantDict.ContainsKey(dir))
                {
                    return merchantDict[dir];
                }
                else return 0;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[getProgramSector]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[getProgramSector] " + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return 0;
            }

        }

        /// <summary>
        /// Gets next available sector
        /// </summary>
        /// <returns>Location of next available sector</returns>
        private int getNextSector()
        {
            try
            {
                String sectors = "";
                sectors = Convert.ToString(sectorsUsed, 2);
                if (_cardType == MIFARE_4K)
                    sectors = sectors.PadLeft(38, '0'); //Mifare 4k total 38 sectors max
                else sectors = sectors.PadLeft(10, '0'); //Mifare 1k total 10 sectors max
                char[] charArr = sectors.ToCharArray();
                Array.Reverse(charArr);
                sectors = new string(charArr);
                return sectors.IndexOf('0', startSector);
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[getNextSector]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[getNextSector] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return 0;
            }
        }

        #region "EPurse"
        /// <summary>
        /// Adds a new ePurse program to the card
        /// </summary>
        /// <param name="data">ePurse structure containing the new purse data</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        private bool addEPurse(ePurse data)
        {
            try
            {
                int nextSector = getProgramSector(data.merchantID, 4);
                if (nextSector == 0) nextSector = getNextSector();

                if (nextSector >= 0)
                {
                    byte[] blk = new byte[16];
                    byte[] shortArr = new byte[2];


                    //Program Type Purse: 4
                    blk[0] = 4 ;

                    //Merchant ID
                    shortArr = BitConverter.GetBytes(data.merchantID);
                    blk[1] = shortArr[0];
                    blk[2] = shortArr[1];

                    //Program Join Date
                    blk[3] = Convert.ToByte(data.programJoinDate.Year - 2000); //Year (2 digits)
                    blk[4] = Convert.ToByte(data.programJoinDate.Month); //Month
                    blk[5] = Convert.ToByte(data.programJoinDate.Day); //Day

                    //Purse Seq Number
                    shortArr = BitConverter.GetBytes(data.purseSeqNumber);
                    blk[6] = shortArr[0];
                    blk[7] = shortArr[1];

                    //Expiry Date
                    blk[8] = Convert.ToByte(data.purseExpiry.Year - 2000); //Year (2 digits)
                    blk[9] = Convert.ToByte(data.purseExpiry.Month); //Month
                    blk[10] = Convert.ToByte(data.purseExpiry.Day); //Day

                    short purseDataBlock = Convert.ToInt16(nextSector * 4);

                    //Authenticate
                    if (!CardComm.AuthBlock(purseDataBlock + 3, get_diversify_key(master,CardComm.currCardIDArr,nextSector)))
                    {
                        if (debug) Log.WriteLog("Error", "[addEPurse]: Authentication Failed for Block " + Convert.ToString(purseDataBlock + 3));
                        lastErr = "Error Authenticating Card!";
                        return false;
                    }
                    //Write block 0 data
                    if (!CardComm.WriteBlock(purseDataBlock, blk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[addEPurse]: Error writing to block" + Convert.ToString(purseDataBlock) + System.Environment.NewLine + CardComm.lastErr);
                        lastErr = "Error Writing to Card";
                        return false;
                    }

                    //Membership ID
                    blk = Encoding.ASCII.GetBytes(data.memberID.PadRight(16).Substring(0, 16));

                    //Write block 1 data
                    if (!CardComm.WriteBlock(purseDataBlock+1, blk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[addEPurse]: Error writing to block" + Convert.ToString(purseDataBlock+1) + System.Environment.NewLine + CardComm.lastErr);
                        lastErr = "Error Writing to Card";
                        return false;
                    }

                    //Initialize Purse
                    byte[] purseArr = BitConverter.GetBytes(data.purseValue);
                    if (!CardComm.WriteBlock(purseDataBlock + 2, purseArr, 1))
                    {
                        if (debug) Log.WriteLog("Error", "[addEPurse]: Error initializing purse"+ System.Environment.NewLine  + CardComm.lastErr);
                        lastErr = "Error Initializing Purse ";
                        return false;
                    }

                    //Update Directory
                    if (updateDirectory(Convert.ToInt16(nextSector), data.merchantID, 4))
                    {
                        //Add ePurse Program to database
                        try{
                            if (debug)
                            {
                                Log.WriteLog("Database", "Executing SP: [dbo].[AddEPurse] '" + cardID + "'," +
                                    Convert.ToString(data.merchantID) + ",'" + data.purseExpiry.ToString("yyyy-MM-dd") + "'");
                            }

                            if (writeToDB)
                            {
                                db.OpenConnection();

                                SqlCommand cmd = db.MakeCommand_SP("[dbo].[AddEPurse]");
                                cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                                cmd.Parameters["@cardIDM"].Value = cardID;
                                cmd.Parameters.Add(new SqlParameter("@merchantID", SqlDbType.VarChar));
                                cmd.Parameters["@merchantID"].Value = data.merchantID;
                                cmd.Parameters.Add(new SqlParameter("@expiry", SqlDbType.VarChar));
                                cmd.Parameters["@expiry"].Value = data.purseExpiry;

                                cmd.ExecuteNonQuery();
                            }
                        }
                        catch(SqlException ex){
                            if (debug) Log.WriteLog("Exception", "[addEPurse]:SQL Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                            lastErr = "[addEPurse] : Error updating database - " + ex.Message;
                            return false;
                        }
                        finally{
                            if (writeToDB) db.CloseConnection();
                        }
                        return true;
                    }
                    else return false;
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[addEPurse]: Card is out of memeory");
                    lastErr = "Out of Memory!";
                    return false;
                }
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[addEPurse]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[addEPurse] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message; 
                return false;
            }
        }

        /// <summary>
        /// Adds a new ePurse program to the card with specified order
        /// </summary>
        /// <param name="data">ePurse structure containing the new purse data</param>
        /// <param name="order">The order of the ePurse program added by the same merchant. i.e. The same merchant can add 2 ePurse programs with order 1 and 2</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        private bool addEPurse(ePurse data, int order)
        {
            try
            {
                int nextSector = getProgramSector(data.merchantID, 4);
                if (nextSector == 0) nextSector = getNextSector();

                if (nextSector >= 0)
                {
                    byte[] blk = new byte[16];
                    byte[] shortArr = new byte[2];


                    //Program Type Purse: 4
                    blk[0] = BitConverter.GetBytes((4 << 4) & order)[0]; ;

                    //Merchant ID
                    shortArr = BitConverter.GetBytes(data.merchantID);
                    blk[1] = shortArr[0];
                    blk[2] = shortArr[1];

                    //Program Join Date
                    blk[3] = Convert.ToByte(data.programJoinDate.Year - 2000); //Year (2 digits)
                    blk[4] = Convert.ToByte(data.programJoinDate.Month); //Month
                    blk[5] = Convert.ToByte(data.programJoinDate.Day); //Day

                    //Purse Seq Number
                    shortArr = BitConverter.GetBytes(data.purseSeqNumber);
                    blk[6] = shortArr[0];
                    blk[7] = shortArr[1];

                    //Expiry Date
                    blk[8] = Convert.ToByte(data.purseExpiry.Year - 2000); //Year (2 digits)
                    blk[9] = Convert.ToByte(data.purseExpiry.Month); //Month
                    blk[10] = Convert.ToByte(data.purseExpiry.Day); //Day

                    short purseDataBlock = Convert.ToInt16(nextSector * 4);

                    //Authenticate
                    if (!CardComm.AuthBlock(purseDataBlock + 3, get_diversify_key(master, CardComm.currCardIDArr, nextSector)))
                    {
                        if (debug) Log.WriteLog("Error", "[addEPurse]: Authentication Failed for Block " + Convert.ToString(purseDataBlock + 3));
                        lastErr = "Error Authenticating Card!";
                        return false;
                    }
                    //Write block 0 data
                    if (!CardComm.WriteBlock(purseDataBlock, blk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[addEPurse]: Error writing to block" + Convert.ToString(purseDataBlock) + System.Environment.NewLine + CardComm.lastErr);
                        lastErr = "Error Writing to Card";
                        return false;
                    }

                    //Membership ID
                    blk = Encoding.ASCII.GetBytes(data.memberID.PadRight(16).Substring(0, 16));

                    //Write block 1 data
                    if (!CardComm.WriteBlock(purseDataBlock + 1, blk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[addEPurse]: Error writing to block" + Convert.ToString(purseDataBlock + 1) + System.Environment.NewLine + CardComm.lastErr);
                        lastErr = "Error Writing to Card";
                        return false;
                    }

                    //Initialize Purse
                    byte[] purseArr = BitConverter.GetBytes(data.purseValue);
                    if (!CardComm.WriteBlock(purseDataBlock + 2, purseArr, 1))
                    {
                        if (debug) Log.WriteLog("Error", "[addEPurse]: Error initializing purse" + System.Environment.NewLine + CardComm.lastErr);
                        lastErr = "Error Initializing Purse ";
                        return false;
                    }

                    //Update Directory
                    if (updateDirectory(Convert.ToInt16(nextSector), data.merchantID, 4))
                    {
                        //Add ePurse Program to database
                        try
                        {
                            if (debug)
                            {
                                Log.WriteLog("Database", "Executing SP: [dbo].[AddEPurse] '" + cardID + "'," +
                                    Convert.ToString(data.merchantID) + ",'" + data.purseExpiry.ToString("yyyy-MM-dd") + "'");
                            }

                            if (writeToDB)
                            {
                                db.OpenConnection();

                                SqlCommand cmd = db.MakeCommand_SP("[dbo].[AddEPurse]");
                                cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                                cmd.Parameters["@cardIDM"].Value = cardID;
                                cmd.Parameters.Add(new SqlParameter("@merchantID", SqlDbType.VarChar));
                                cmd.Parameters["@merchantID"].Value = data.merchantID;
                                cmd.Parameters.Add(new SqlParameter("@expiry", SqlDbType.VarChar));
                                cmd.Parameters["@expiry"].Value = data.purseExpiry;

                                cmd.ExecuteNonQuery();
                            }
                        }
                        catch (SqlException ex)
                        {
                            if (debug) Log.WriteLog("Exception", "[addEPurse]:SQL Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                            lastErr = "[addEPurse] : Error updating database - " + ex.Message;
                            return false;
                        }
                        finally
                        {
                            if (writeToDB) db.CloseConnection();
                        }
                        return true;
                    }
                    else return false;
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[addEPurse]: Card is out of memeory");
                    lastErr = "Out of Memory!";
                    return false;
                }
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[addEPurse]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[addEPurse] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }

        /// <summary>
        /// Reads ePurse data
        /// </summary>
        /// <param name="sect">Sector to read</param>
        /// <param name="pData">Holds the purse data to be read from the card</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        private bool readEPurse(short sect, ref ePurse pData)
        {
            byte[] data = new byte[16];

            try
            {
                if (CardComm.currCardID == String.Empty)
                {
                    if (debug) Log.WriteLog("Error", "[readEPurse]: No Card Detected");
                    lastErr = "No Card Detected!";
                    return false;
                }

                if (!CardComm.AuthBlock((sect * 4) + 3, get_diversify_key(master,CardComm.currCardIDArr,sect)))
                {
                    if (debug) Log.WriteLog("Error", "[readEPurse]: Authentication Failed for Block " + Convert.ToString((sect * 4) + 3));
                    lastErr = "Error Authenticating Card!";
                    return false;
                }
                if (CardComm.ReadBlock(sect * 4, data, 0))
                {
                    if (data[0] != 4)
                    {
                        if (debug) Log.WriteLog("Error", "[readEPurse]: Sector is not being used for ePurse program. Data read: " + Convert.ToString(data[0]));
                        lastErr = "Sector is not being used for ePurse program!";
                        return false;
                    }
                    pData.merchantID = BitConverter.ToInt16(data, 1);
                    pData.programJoinDate = new DateTime(data[3] + 2000, data[4], data[5]);
                    pData.purseSeqNumber = BitConverter.ToInt16(data, 6);
                    pData.purseExpiry = new DateTime(data[8] + 2000, data[9], data[10]);
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[readEPurse]: Error reading block " + Convert.ToString(sect * 4) + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                if (CardComm.ReadBlock((sect * 4) + 1, data, 0))
                {
                    pData.memberID = Encoding.ASCII.GetString(data, 0, 16);
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[readEPurse]: Error reading block " + Convert.ToString(sect * 4 + 1) + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                byte[] purseArr = new byte[4];
                if (!CardComm.ReadBlock(sect * 4 + 2, purseArr, 1))
                {
                    if (debug) Log.WriteLog("Error", "[readEPurse]: Error reading block " + Convert.ToString(sect * 4+2) + System.Environment.NewLine + CardComm.lastErr);
                    lastErr = "Error reading purse!";
                    return false;
                }
                else
                {
                    Array.Reverse(purseArr, 0, 4);
                    pData.purseValue = BitConverter.ToInt32(purseArr, 0);
                }


                return true;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[readEPurse]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[readEPurse] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }

        /// <summary>
        /// Edits purse data (Top up/deduct value, change expiry date)
        /// </summary>
        /// <param name="sect">Sector containing the data to be edited</param>
        /// <param name="amt">Amount to top up/deduct. Deduction amount is negative.</param>
        /// <param name="expiry">Expiry date</param>
        /// <param name="merchantID">ID of the merchant where the transaction was performed</param>
        /// <param name="memberID">Member ID of the card holder</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        private bool editEPurse(short sect, int amt, DateTime expiry, int merchantID, string memberID)
        {
            ePurse pdata = new ePurse();
            try
            {
                if (!readEPurse(sect, ref pdata)) return false;

                if (amt != 0)
                    pdata.purseSeqNumber = Convert.ToInt16(pdata.purseSeqNumber + 1);
                if (pdata.purseValue + amt < 0)
                {
                    if (debug) Log.WriteLog("Error", "[editEPurse]: New purse balance cannot be negative!");
                    lastErr = "New purse balance cannot be negative!";
                    return false;
                }
                pdata.purseExpiry = expiry;

                byte[] blk = new byte[16];
                byte[] shortArr = new byte[2];

                //Program Type Purse: 4
                blk[0] = 4;

                //Merchant ID
                shortArr = BitConverter.GetBytes(pdata.merchantID);
                blk[1] = shortArr[0];
                blk[2] = shortArr[1];

                //Program Join Date
                blk[3] = Convert.ToByte(pdata.programJoinDate.Year - 2000); //Year (2 digits)
                blk[4] = Convert.ToByte(pdata.programJoinDate.Month); //Month
                blk[5] = Convert.ToByte(pdata.programJoinDate.Day); //Day

                //Purse Seq Number
                shortArr = BitConverter.GetBytes(pdata.purseSeqNumber);
                blk[6] = shortArr[0];
                blk[7] = shortArr[1];

                //Expiry Date
                blk[8] = Convert.ToByte(pdata.purseExpiry.Year - 2000); //Year (2 digits)
                blk[9] = Convert.ToByte(pdata.purseExpiry.Month); //Month
                blk[10] = Convert.ToByte(pdata.purseExpiry.Day); //Day

                //Authenticate
                if (!CardComm.AuthBlock((sect * 4) + 3, get_diversify_key(master,CardComm.currCardIDArr,sect)))
                {
                    if (debug) Log.WriteLog("Error", "[editEPurse]: Authentication Failed for Block " + Convert.ToString((sect * 4) + 3));
                    lastErr = "Error Authenticating Card!";
                    return false;
                }

                //Write block 0 data
                if (!CardComm.WriteBlock(sect * 4, blk, 0))
                {
                    if (debug) Log.WriteLog("Error", "[editEPurse]: Error writing block " + Convert.ToString(sect * 4) + System.Environment.NewLine + CardComm.lastErr);
                    lastErr = "Error writing to card";
                    return false;
                }

                //Membership ID
                blk = Encoding.ASCII.GetBytes(memberID.PadRight(16).Substring(0, 16));

                //Write block 1 data
                if (!CardComm.WriteBlock(sect * 4 + 1, blk, 0))
                {
                    if (debug) Log.WriteLog("Error", "[editEPurse]: Error writing block " + Convert.ToString(sect * 4 +1) + System.Environment.NewLine + CardComm.lastErr);
                    lastErr = "Error writing to card";
                    return false;
                }

                if (amt != 0)
                {
                    //Increase/Decrease
                    byte[] purseArr = new byte[4];
                    if (amt > 0)
                    {
                        purseArr = BitConverter.GetBytes(amt);
                        if (!CardComm.WriteBlock(sect * 4 + 2, purseArr, 2))
                        {
                            if (debug) Log.WriteLog("Error", "[editEPurse]: Error increasing purse value (block " + Convert.ToString(sect * 4 + 2) + ")\n" + CardComm.lastErr);
                            lastErr = "Error increasing purse value";
                            return false;
                        }
                    }
                    else
                    {
                        amt = amt * -1;
                        purseArr = BitConverter.GetBytes(amt);
                        if (!CardComm.WriteBlock(sect * 4 + 2, purseArr, 3))
                        {
                            if (debug) Log.WriteLog("Error", "[editEPurse]: Error deducting purse value (block " + Convert.ToString(sect * 4 + 2) + ")\n" + CardComm.lastErr);
                            lastErr = "Error deducting purse value";
                            return false;
                        }
                    }
                }

                if (writeToDB)
                {

                    try
                    {
                        db.OpenConnection();
                        SqlCommand cmd = db.MakeCommand_SP("[dbo].[TopupDeductEPurse]");
                        cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                        cmd.Parameters["@cardIDM"].Value = cardID;
                        cmd.Parameters.Add(new SqlParameter("@merchantID", SqlDbType.Int));
                        cmd.Parameters["@merchantID"].Value = merchantID;
                        cmd.Parameters.Add(new SqlParameter("@txnvalue", SqlDbType.Int));
                        cmd.Parameters["@txnvalue"].Value = amt;
                        cmd.Parameters.Add(new SqlParameter("@newbalance", SqlDbType.Int));
                        cmd.Parameters["@newbalance"].Value = pdata.purseValue + amt;
                        cmd.Parameters.Add(new SqlParameter("@purseseq", SqlDbType.Int));
                        cmd.Parameters["@purseseq"].Value = pdata.purseSeqNumber;

                        cmd.ExecuteNonQuery();
                    }

                    catch (SqlException ex)
                    {

                        if (debug) Log.WriteLog("Exception", "[editEPurse]:SQL Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                        lastErr = "[editEPurse] : Error updating database - " + ex.Message;
                        return false;
                    }
                    finally
                    {
                        if(writeToDB) db.CloseConnection();
                    }
                }
                return true;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[editEPurse]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[editEPurse] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }
        #endregion

        #region "Loyalty"
        /// <summary>
        /// Adds a new loyalty program to the card
        /// </summary>
        /// <param name="data">LoyaltyData structure containing the new loyalty data </param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        private bool addLoyalty(LoyaltyData data)
        {
            try
            {
                int nextSector = getProgramSector(data.merchantID, 2,1);
                if (nextSector == 0) nextSector = getNextSector();


                if (nextSector >= 0)
                {

                    byte[] blk = new byte[16];
                    byte[] shortArr = new byte[2];
                    byte[] intArr = new byte[4];

                    //Program Type Loyalty: 2
                    blk[0] = BitConverter.GetBytes((2 << 4) | 1)[0];

                    //Merchant ID
                    shortArr = BitConverter.GetBytes(data.merchantID);
                    blk[1] = shortArr[0];
                    blk[2] = shortArr[1];

                    //Program Join Date
                    blk[3] = Convert.ToByte(data.programJoinDate.Year - 2000); //Year (2 digits)
                    blk[4] = Convert.ToByte(data.programJoinDate.Month); //Month
                    blk[5] = Convert.ToByte(data.programJoinDate.Day); //Day

                    //Points Seq Number
                    shortArr = BitConverter.GetBytes(data.pointsSeq);
                    blk[6] = shortArr[0];
                    blk[7] = shortArr[1];

                    //Points
                    intArr = BitConverter.GetBytes(data.points);
                    blk[8] = intArr[0];
                    blk[9] = intArr[1];
                    blk[10] = intArr[2];
                    blk[11] = intArr[3];

                    //Expiry Date
                    blk[12] = Convert.ToByte(data.expiryDate.Year - 2000); //Year (2 digits)
                    blk[13] = Convert.ToByte(data.expiryDate.Month); //Month
                    blk[14] = Convert.ToByte(data.expiryDate.Day); //Day

                    short loyaltyDataBlock = Convert.ToInt16(nextSector * 4);

                    //Authenticate
                    if (!CardComm.AuthBlock(loyaltyDataBlock + 3, get_diversify_key(master,CardComm.currCardIDArr,nextSector)))
                    {
                        if (debug) Log.WriteLog("Error", "[addLoyalty]: Authentication Failed for Block " + Convert.ToString(loyaltyDataBlock + 3));
                        lastErr = "Error Authenticating Card!";
                        return false;
                    }
                    //Write block 0 data
                    if (!CardComm.WriteBlock(loyaltyDataBlock, blk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[addLoyalty]: Error writing block " + Convert.ToString(loyaltyDataBlock) + System.Environment.NewLine + CardComm.lastErr);
                        lastErr = "Error Writing to Card";
                        return false;
                    }

                    //Membership ID
                    blk = Encoding.ASCII.GetBytes(data.memberID.PadRight(16).Substring(0, 16));

                    //Write block 1 data
                    if (!CardComm.WriteBlock(loyaltyDataBlock+1, blk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[addLoyalty]: Error writing block " + Convert.ToString(loyaltyDataBlock+1) + System.Environment.NewLine + CardComm.lastErr);
                        lastErr = "Error Writing to Card";
                        return false;
                    }

                    //Update Directory
                    if (updateDirectory(Convert.ToInt16(nextSector), data.merchantID, BitConverter.GetBytes((2 << 4) | 1)[0]))
                    {
                        if (debug)
                        {
                            Log.WriteLog("Database", "Executing SP: [dbo].[AddLoyalty] '" + cardID + "'," +
                                Convert.ToString(data.merchantID) + ",'" + data.expiryDate.ToString("yyyy-MM-dd") + "'");
                        }
                        if (writeToDB)
                        {
                            //Add loyalty program to database
                            try
                            {
                                db.OpenConnection();
                                SqlCommand cmd = db.MakeCommand_SP("[dbo].[AddLoyalty]");
                                cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                                cmd.Parameters["@cardIDM"].Value = cardID;
                                cmd.Parameters.Add(new SqlParameter("@merchantID", SqlDbType.VarChar));
                                cmd.Parameters["@merchantID"].Value = data.merchantID;
                                cmd.Parameters.Add(new SqlParameter("@expiry", SqlDbType.VarChar));
                                cmd.Parameters["@expiry"].Value = data.expiryDate;
                                cmd.ExecuteNonQuery();

                            }
                            catch (SqlException ex)
                            {
                                if (debug) Log.WriteLog("Exception", "[addEPurse]:SQL Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);

                                lastErr = "[addEPurse] : Error updating database - " + ex.Message;
                                return false;
                            }
                            finally
                            {
                                if (writeToDB) db.CloseConnection();
                            }
                        }
                        return true;
                    }
                    else
                    {
                        if (debug) Log.WriteLog("Error", "[addEPurse]: Error updating directory" + System.Environment.NewLine + CardComm.lastErr);
                        return false;
                    }

                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[addLoyalty]: Card has insufficient memory");
                    lastErr = "Out of Memory!";
                    return false;
                }
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[addLoyalty]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[addLoyalty] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }

        /// <summary>
        /// Adds a new loyalty program to the card with the specified order
        /// </summary>
        /// <param name="data">LoyaltyData structure containing the new loyalty data </param>
        /// <param name="order">The order of the loyalty program added by the same merchant. i.e. The same merchant can add 2 loyalty programs with order 1 and 2</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        private bool addLoyalty(LoyaltyData data, short order)
        {
            try
            {
                int nextSector = getProgramSector(data.merchantID, 2,order);
                if (nextSector == 0) nextSector = getNextSector();
                

                if (nextSector >= 0)
                {

                    byte[] blk = new byte[16];
                    byte[] shortArr = new byte[2];
                    byte[] intArr = new byte[4];

                    //Program Type Loyalty: 2
                    blk[0] = BitConverter.GetBytes((2 << 4) | order)[0];

                    //Merchant ID
                    shortArr = BitConverter.GetBytes(data.merchantID);
                    blk[1] = shortArr[0];
                    blk[2] = shortArr[1];

                    //Program Join Date
                    blk[3] = Convert.ToByte(data.programJoinDate.Year - 2000); //Year (2 digits)
                    blk[4] = Convert.ToByte(data.programJoinDate.Month); //Month
                    blk[5] = Convert.ToByte(data.programJoinDate.Day); //Day

                    //Points Seq Number
                    shortArr = BitConverter.GetBytes(data.pointsSeq);
                    blk[6] = shortArr[0];
                    blk[7] = shortArr[1];

                    //Points
                    intArr = BitConverter.GetBytes(data.points);
                    blk[8] = intArr[0];
                    blk[9] = intArr[1];
                    blk[10] = intArr[2];
                    blk[11] = intArr[3];

                    //Expiry Date
                    blk[12] = Convert.ToByte(data.expiryDate.Year - 2000); //Year (2 digits)
                    blk[13] = Convert.ToByte(data.expiryDate.Month); //Month
                    blk[14] = Convert.ToByte(data.expiryDate.Day); //Day

                    short loyaltyDataBlock = Convert.ToInt16(nextSector * 4);

                    //Authenticate
                    if (!CardComm.AuthBlock(loyaltyDataBlock + 3, get_diversify_key(master, CardComm.currCardIDArr, nextSector)))
                    {
                        if (debug) Log.WriteLog("Error", "[addLoyalty]: Authentication Failed for Block " + Convert.ToString(loyaltyDataBlock + 3));
                        lastErr = "Error Authenticating Card!";
                        return false;
                    }
                    //Write block 0 data
                    if (!CardComm.WriteBlock(loyaltyDataBlock, blk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[addLoyalty]: Error writing block " + Convert.ToString(loyaltyDataBlock) + System.Environment.NewLine + CardComm.lastErr);
                        lastErr = "Error Writing to Card";
                        return false;
                    }

                    //Membership ID
                    blk = Encoding.ASCII.GetBytes(data.memberID.PadRight(16).Substring(0, 16));

                    //Write block 1 data
                    if (!CardComm.WriteBlock(loyaltyDataBlock + 1, blk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[addLoyalty]: Error writing block " + Convert.ToString(loyaltyDataBlock + 1) + System.Environment.NewLine + CardComm.lastErr);
                        lastErr = "Error Writing to Card";
                        return false;
                    }

                    //Update Directory
                    if (updateDirectory(Convert.ToInt16(nextSector), data.merchantID, BitConverter.GetBytes((2 << 4) | order)[0]))
                    {
                        if (debug)
                        {
                            Log.WriteLog("Database", "Executing SP: [dbo].[AddLoyalty] '" + cardID + "'," +
                                Convert.ToString(data.merchantID) + ",'" + data.expiryDate.ToString("yyyy-MM-dd") + "'");
                        }
                        if (writeToDB)
                        {
                            //Add loyalty program to database
                            try
                            {
                                db.OpenConnection();
                                SqlCommand cmd = db.MakeCommand_SP("[dbo].[AddLoyalty]");
                                cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                                cmd.Parameters["@cardIDM"].Value = cardID;
                                cmd.Parameters.Add(new SqlParameter("@merchantID", SqlDbType.VarChar));
                                cmd.Parameters["@merchantID"].Value = data.merchantID;
                                cmd.Parameters.Add(new SqlParameter("@expiry", SqlDbType.VarChar));
                                cmd.Parameters["@expiry"].Value = data.expiryDate;
                                cmd.ExecuteNonQuery();

                            }
                            catch (SqlException ex)
                            {
                                if (debug) Log.WriteLog("Exception", "[addEPurse]:SQL Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);

                                lastErr = "[addEPurse] : Error updating database - " + ex.Message;
                                return false;
                            }
                            finally
                            {
                                if (writeToDB) db.CloseConnection();
                            }
                        }
                        return true;
                    }
                    else
                    {
                        if (debug) Log.WriteLog("Error", "[addEPurse]: Error updating directory" + System.Environment.NewLine + CardComm.lastErr);
                        return false;
                    }

                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[addLoyalty]: Card has insufficient memory");
                    lastErr = "Out of Memory!";
                    return false;
                }
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[addLoyalty]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[addLoyalty] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }

        /// <summary>
        /// Reads loyalty data from the card
        /// </summary>
        /// <param name="sect">Sector containing the loyalty data to be read</param>
        /// <param name="ldata">Holds the loyalty data to be read from the card</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        private bool readLoyalty(short sect, ref LoyaltyData ldata)
        {
            byte[] data = new byte[16];

            try
            {
                if (CardComm.currCardID == String.Empty)
                {
                    if (debug) Log.WriteLog("Error", "[readLoyalty]: No Card Detected");
                    lastErr = "No Card Detected!";
                    return false;
                }

                if (!CardComm.AuthBlock((sect * 4) + 3, get_diversify_key(master,CardComm.currCardIDArr,sect)))
                {
                    if (debug) Log.WriteLog("Error", "[readLoyalty]: Authentication Failed for Block " + Convert.ToString((sect * 4) + 3));
                    lastErr = "Error Authenticating Card!";
                    return false;
                }
                if (CardComm.ReadBlock(sect * 4, data, 0))
                {
                    if (data[0]>>4 != 2)
                    {
                        if (debug) Log.WriteLog("Error", "[readLoyalty]: Sector is not being used for Loyalty program. Data read: " + Convert.ToString(data[0]));
                        lastErr = "Sector is not being used for Loyalty program!";
                        return false;
                    }

                    ldata.merchantID = BitConverter.ToInt16(data, 1);
                    ldata.order = Convert.ToInt16(data[0] & 15); 
                    ldata.programJoinDate = new DateTime(data[3] + 2000, data[4], data[5]);
                    ldata.pointsSeq = BitConverter.ToInt16(data, 6);
                    ldata.points = BitConverter.ToInt32(data, 8);
                    ldata.expiryDate = new DateTime(data[12] + 2000, data[13], data[14]);
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[readLoyalty]: Error reading block " + Convert.ToString(sect * 4) + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                if (CardComm.ReadBlock((sect * 4) + 1, data, 0))
                {
                    ldata.memberID = Encoding.ASCII.GetString(data, 0, 16);
                    return true;
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[readLoyalty]: Error reading block " + Convert.ToString(sect * 4 +1) + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[readLoyalty]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[readLoyalty] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }

        /// <summary>
        /// Edit the loyalty data in the card (Add/Deduct points, change expiry date)
        /// </summary>
        /// <param name="sect">Sector in which the program is stored</param>
        /// <param name="order">Order of the loyalty program for this merchant</param>
        /// <param name="points">Points to be added/deducted. For deduction, points should be negative</param>
        /// <param name="expiry">Expiry date</param>
        /// <param name="merchantID">ID of the merchant where the transaction was performed</param>
        /// <param name="memberID">Member ID of the cardholder</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        private bool editLoyalty(short sect, short order, int points, DateTime expiry, int merchantID, string memberID)
        {
            LoyaltyData ldata = new LoyaltyData();
            try
            {
                if (readLoyalty(sect, ref ldata) == false) return false;

                if (points != 0)
                    ldata.pointsSeq = Convert.ToInt16(ldata.pointsSeq + 1);
                ldata.points = ldata.points + points;
                if (ldata.points < 0)
                {
                    if (debug) Log.WriteLog("Error", "[editLoyalty]: New points balance cannot be negative");
                    lastErr = "New points balance cannot be negative!";
                    return false;
                }
                ldata.expiryDate = expiry;

                byte[] blk = new byte[16];
                byte[] shortArr = new byte[2];
                byte[] intArr = new byte[4];

                //Program Type Loyalty: 2
                blk[0] = BitConverter.GetBytes((2<<4) | order)[0];

                //Merchant ID
                shortArr = BitConverter.GetBytes(ldata.merchantID);
                blk[1] = shortArr[0];
                blk[2] = shortArr[1];

                //Program Join Date
                blk[3] = Convert.ToByte(ldata.programJoinDate.Year - 2000); //Year (2 digits)
                blk[4] = Convert.ToByte(ldata.programJoinDate.Month); //Month
                blk[5] = Convert.ToByte(ldata.programJoinDate.Day); //Day

                //Points Seq Number
                shortArr = BitConverter.GetBytes(ldata.pointsSeq);
                blk[6] = shortArr[0];
                blk[7] = shortArr[1];

                //Points
                intArr = BitConverter.GetBytes(ldata.points);
                blk[8] = intArr[0];
                blk[9] = intArr[1];
                blk[10] = intArr[2];
                blk[11] = intArr[3];

                //Expiry Date
                blk[12] = Convert.ToByte(ldata.expiryDate.Year - 2000); //Year (2 digits)
                blk[13] = Convert.ToByte(ldata.expiryDate.Month); //Month
                blk[14] = Convert.ToByte(ldata.expiryDate.Day); //Day

                //Authenticate
                if (!CardComm.AuthBlock((sect * 4) + 3, get_diversify_key(master,CardComm.currCardIDArr,sect)))
                {
                    if (debug) Log.WriteLog("Error", "[editLoyalty]: Authentication Failed for Block " + Convert.ToString((sect * 4) + 3));
                    lastErr = "Error Authenticating Card!";
                    return false;
                }

                //Write block 0 data
                if (!CardComm.WriteBlock(sect * 4, blk, 0))
                {
                    if (debug) Log.WriteLog("Error", "[editLoyalty]: Error writing block " + Convert.ToString(sect * 4) + System.Environment.NewLine + CardComm.lastErr);
                    lastErr = "Error writing to card";
                    return false;
                }

                //Member ID
                blk = Encoding.ASCII.GetBytes(memberID.PadRight(16).Substring(0, 16));
                //Write block 1 data
                if (!CardComm.WriteBlock(sect * 4 +1, blk, 0))
                {
                    if (debug) Log.WriteLog("Error", "[editLoyalty]: Error writing block " + Convert.ToString(sect * 4 +1) + System.Environment.NewLine + CardComm.lastErr);
                    lastErr = "Error writing to card";
                    return false;
                }

                //Add loyalty txn to database
                if (points != 0)
                {
                    if (debug)
                    {
                        Log.WriteLog("Database", "Executing SP: [dbo].[TopupDeductLoyalty] '" + cardID + "'" +
                            "," + Convert.ToString(merchantID) + "," + Convert.ToString(points) + "," + Convert.ToString(ldata.points) +
                            "," + Convert.ToString(ldata.pointsSeq));
                    }
                    if (writeToDB)
                    {
                        try
                        {

                            db.OpenConnection();
                            SqlCommand cmd = db.MakeCommand_SP("[dbo].[TopupDeductLoyalty]");
                            cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                            cmd.Parameters["@cardIDM"].Value = cardID;
                            cmd.Parameters.Add(new SqlParameter("@merchantID", SqlDbType.Int));
                            cmd.Parameters["@merchantID"].Value = merchantID;
                            cmd.Parameters.Add(new SqlParameter("@txnvalue", SqlDbType.Int));
                            cmd.Parameters["@txnvalue"].Value = points;
                            cmd.Parameters.Add(new SqlParameter("@newbalance", SqlDbType.Int));
                            cmd.Parameters["@newbalance"].Value = ldata.points;
                            cmd.Parameters.Add(new SqlParameter("@pointsseq", SqlDbType.Int));
                            cmd.Parameters["@pointsseq"].Value = ldata.pointsSeq;

                            cmd.ExecuteNonQuery();
                        }
                        catch (SqlException ex)
                        {

                            if (debug) Log.WriteLog("Exception", "[editLoyalty]:SQL Exception", ex.Message + System.Environment.NewLine + ex.InnerException);
                            lastErr = "[editEPurse] : Error updating database - " + ex.Message;
                            return false;
                        }
                        finally
                        {
                            if (writeToDB) db.CloseConnection();
                        }
                    }
                }
                return true;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[editLoyalty]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[editLoyalty] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }
        #endregion

        /// <summary>
        /// Issues a new card and activates the loyalty points and ePurse functions
        /// </summary>
        /// <param name="pname">Name of the card holder</param>
        /// <param name="pDOB">Date of Birth</param>
        /// <param name="pgender">Gender - M: Male, F: Female</param>
        /// <param name="pmerchantID">Unique ID of the merchant issuing the card</param>
        /// <param name="pcardNum">Unique card number identifying the card</param>
        /// <param name="pterminalID">Terminal ID of the terminal which the card is issued from</param>
        /// <param name="pdata">Initial purse data of the card</param>
        /// <param name="ldata">Initial loyalty data of the card</param>
        /// <param name="pID">IC/Passport number of the card holder</param>
        /// <param name="pphone">Phone number of the card holder</param>
        /// <param name="pemail">Email address of the card holder</param>
        /// <param name="status">Card Status</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        public bool issueCard(string pname, DateTime pDOB, char pgender, short pmerchantID, int pcardNum, short pterminalID, ePurse pdata, LoyaltyData ldata, string pID, string pphone, string pemail, byte status)
        {
            try
            {
                if (debug) Log.WriteLog("Invoke Method", "issueCard(" + pname + "," + pDOB.ToString("yyyy-MM-dd") +
                    "," + pgender + "," + Convert.ToString(pmerchantID) + "," + Convert.ToString(pcardNum) + "," + Convert.ToString(pterminalID) +
                    ",[ePurse data - Merchant ID:" + Convert.ToString(pdata.merchantID) + ", Join Date:" + pdata.programJoinDate.ToString("yyyy-MM-dd") +
                    ", Expiry Date: " + pdata.purseExpiry.ToString("yyyy-MM-dd") + ", Purse Value:" + Convert.ToString(pdata.purseValue) + "], [Loyalty data - Merchant ID:" +
                    Convert.ToString(ldata.merchantID) + ", Join Date:" + ldata.programJoinDate + ", Expiry Date: " + ldata.expiryDate +
                    ", Points: " + ldata.points + "," + Convert.ToString(pID) + "," + pphone + "," + pemail + "," + status.ToString() + ")");

                if (_cardType == MIFARE_1K)
                {
                    if (!initialize1KCard(pname, pDOB, pgender, pmerchantID, pcardNum, pterminalID, pID, pphone, pemail, status))
                        return false;
                }
                else if (_cardType == MIFARE_4K)
                {
                    if (!initialize4KCard(pname, pDOB, pgender, pmerchantID, pcardNum, pterminalID, pID, pphone, pemail, status))
                        return false;
                }
                merchantDict.Clear();
                sectorsUsed = 0;
                if (addEPurse(pdata))
                {
                    if (readCard())
                    {
                        for (int i = 0; i < 5; i++)
                        {
                            ldata.order = Convert.ToInt16(i + 1);
                            if (!addLoyalty(ldata, ldata.order)) return false;
                            else
                            {
                                if (!readCard()) return false;
                            }
                        }
                        return true;
                    }
                    else return false;
                }
                else return false;
                
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[issueCard]:Exception" + ex.Message, ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[issueCard] " + ex.Message +(ex.InnerException == null ? "" : ":" + ex.InnerException.Message);
                return false;
            }
        }

        /// <summary>
        /// Reads the floor and room IDs where the card holder is staying
        /// </summary>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        public bool readRoomCard()
        {
            //Read sector 1 blk 0
            try
            {
                byte[] data = new byte[16];
                byte[] shortArr = new byte[2];
                if (CardComm.AuthBlock(7, get_diversify_key(master,CardComm.currCardIDArr,1)))
                {
                    if (CardComm.ReadBlock(5, data, 0))
                    {
                        _roomID = data[0];
                        shortArr[0] = data[2];
                        shortArr[1] = data[1];
                        _bedID = BitConverter.ToInt16(shortArr,0);
                        if (roomList.ContainsKey(_roomID))
                            _roomName = roomList[_roomID];
                        else
                            _roomName = "Unable to retrieve room name from settings file";
                        return true;
                    }
                    else
                    {
                        if (debug) Log.WriteLog("Error", "[readRoomCard]: Error reading block 4" + System.Environment.NewLine + CardComm.lastErr);
                        return false;
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[readRoomCard]: Error authenticating block 7" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }
            }
            catch (Exception ex)
            {

                if (debug) Log.WriteLog("Exception", "[readRoomCard]:Exception", ex.Message + (ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message));
                lastErr = "[readRoomCard] " + ex.Message + (ex.InnerException == null ? "" : ":" + ex.InnerException.Message);
                return false;
            }
        }

        /// <summary>
        /// Reads the purse value of the card
        /// </summary>
        /// <param name="merchantID">Unique ID of the merchant issuing the card</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        public bool readPurseCard(short merchantID)
        {
            try
            {
                if (readCard())
                {
                    short sector = getProgramSector(merchantID, 4);
                    if (readEPurse(Convert.ToInt16(sector), ref _purse))
                        return true;
                }
                return false;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[readPurseCard]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[readPurseCard] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }

        /// <summary>
        /// Reads loyalty data from the card
        /// </summary>
        /// <param name="merchantID">Unique ID of the merchant issuing the card</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        public bool readLoyaltyCard(short merchantID)
        {
            try
            {
                LoyaltyData data = new LoyaltyData();
                int order = 0;
                short sector = 0;
                if (readCard())
                {
                    _loyalty.Clear();
                    foreach(KeyValuePair<DirData,short> item in merchantDict)
                    {
                        if (item.Key.programType >> 4 == 2)
                        {
                            order = (item.Key.programType & 15);
                            sector = getProgramSector(merchantID, 2,Convert.ToInt16(order));
                            if (readLoyalty(Convert.ToInt16(sector), ref data))
                                _loyalty.Add(data);
                            else
                            {
                                _loyalty.Clear();
                                return false;
                            }
                        }
                    }
                    return true;                   

                }
                else return false;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[readLoyaltyCard]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[readLoyaltyCard] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }

        }        

        /// <summary>
        /// Top up or deduct money from the card
        /// </summary>
        /// <param name="merchantID">Unique ID of the merchant issuing the card</param>
        /// <param name="amt">Amount in cents. E.g. $5.00 = 500, $2.50 = 250 </param>
        /// <param name="topup">True if it is a top up operation, False if it is a deduction operation</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        public bool topupDeductPurse(short merchantID, int amt, bool topup)
        {
            try
            {
                if (debug) Log.WriteLog("Invoke Method", "topupDeductPurse(" + Convert.ToString(merchantID) + "," +
                     Convert.ToString(amt) + "," + Convert.ToString(topup) + ")");

                if (readPurseCard(merchantID))
                {
                    short sector = getProgramSector(merchantID, 4);
                    if (!topup) amt = amt * -1;
                    if (editEPurse(Convert.ToInt16(sector), amt, this._purse.purseExpiry, merchantID, issueCardID.ToString()))
                        return true;
                }
                return false;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[topupDeductPurse]:Exception", ex.Message + (ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message));
                lastErr = "[topupDeductPurse] " + ex.Message +(ex.InnerException == null ? "" : ":" + ex.InnerException.Message);
                return false;
            }
        }

        /// <summary>
        /// Top up or deduct loyalty points from the card
        /// </summary>
        /// <param name="merchantID">Unique ID of the merchant issuing the card</param>
        /// <param name="points">Number of points to top up/deduct</param>
        /// <param name="topup">True if it is a top up operation, False if it is a deduction operation</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        public bool topupDeductPoints(short merchantID, int points, bool topup)
        {
            try
            {
                if (debug) Log.WriteLog("Invoke Method", "topupDeductPoints(" + Convert.ToString(merchantID) + "," +
                       Convert.ToString(points) + "," + Convert.ToString(topup) + ")");
                
                if (readLoyaltyCard(merchantID))
                {
                    short sector = getProgramSector(merchantID, 2);
                    if (!topup) points = points * -1;
                    if (editLoyalty(Convert.ToInt16(sector), 1, points, this._loyalty[0].expiryDate, merchantID, issueCardID.ToString()))
                        return true;
                }

                return false;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[topupDeductPoints]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[topupDeductPoints] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }

        }

        /// <summary>
        /// Top up or deduct loyalty points from the card
        /// </summary>
        /// <param name="merchantID">Unique ID of the merchant issuing the card</param>
        /// <param name="points">Number of points to top up/deduct</param>
        /// <param name="order">Order of the loyalty program to top up/deduct points from/to</param>
        /// <param name="topup">True if it is a top up operation, False if it is a deduction operation</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        public bool topupDeductPoints(short merchantID, short order, int points, bool topup)
        {
            try
            {
                if (debug) Log.WriteLog("Invoke Method", "topupDeductPoints(" + Convert.ToString(merchantID) + "," + Convert.ToString(order) + "," +
                       Convert.ToString(points) + "," + Convert.ToString(topup) + ")");

                if (readLoyaltyCard(merchantID))
                {
                    short sector = getProgramSector(merchantID, 2, order);
                    if (sector != 0)
                    {
                        if (!topup) points = points * -1;
                        if (editLoyalty(Convert.ToInt16(sector), order, points, this._loyalty[0].expiryDate, merchantID, issueCardID.ToString()))
                            return true;
                    }
                    else
                    {
                        lastErr = "[topupDeductPoints] Specified order does not exist";
                        return false;
                    }
                }

                return false;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[topupDeductPoints]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[topupDeductPoints] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }

        }
        /// <summary>
        /// Gets the 4 byte card ID of the MIFARE card
        /// </summary>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        public bool getCardID()
        {
            try
            {
                if (CardComm.ConnectCard())
                {
                    _cardID = CardComm.currCardID;
                    if (CardComm.cardType[0] == CardComm.MIFARE_1K[0] && CardComm.cardType[1] == CardComm.MIFARE_1K[1]) _cardType = MIFARE_1K;
                    else if (CardComm.cardType[0] == CardComm.MIFARE_4K[0] && CardComm.cardType[1] == CardComm.MIFARE_4K[1]) _cardType = MIFARE_4K;

                    return true;
                }
                else lastErr = CardComm.lastErr;
                return false;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[getCardID]:Exception", ex.Message + (ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message));
                lastErr = "[getCardID] " + ex.Message + (ex.InnerException == null ? "" : ":" + ex.InnerException.Message);
                return false;
            }
        }

        private static byte[] get_diversify_key(byte[] master_key, byte[] csn, int sector_no)
        {
            byte[] x = new byte[6];

            x[0] = Convert.ToByte((master_key[0] + (csn[0] * (sector_no + 0))) % 256);
            x[1] = Convert.ToByte((master_key[1] + (csn[1] * (sector_no + 1))) % 256);
            x[2] = Convert.ToByte((master_key[2] + (csn[2] * (sector_no + 2))) % 256);
            x[3] = Convert.ToByte((master_key[3] + (csn[3] * (sector_no + 3))) % 256);
            x[4] = Convert.ToByte((master_key[4] + (master_key[0] * (sector_no + 4))) % 256);
            x[5] = Convert.ToByte((master_key[5] + (master_key[1] * (sector_no + 5))) % 256);

            return x;
        }

        private void initializeRooms()
        {
            try
            {
                string appDir = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
                //check the file
                if (!File.Exists(appDir + "\\roomSettings.txt"))
                {
                    if (debug) Log.WriteLog("Error", "[initializeRooms]: Room Settings file (roomSettings.txt) does not exist in application directory");
                    lastErr = "Room Settings file (roomSettings.txt) does not exist in application directory!";
                    return;
                }
                FileStream fs = new FileStream(appDir + "\\roomSettings.txt", FileMode.Open, FileAccess.Read);
                StreamReader sr = new StreamReader(fs);
                string currRec = "";
                int currRmID = 0;
                string currRmName = "";
                string[] temp;

                while (!sr.EndOfStream)
                {
                    currRec = sr.ReadLine();
                    temp = currRec.Split(';');
                    currRmID = Convert.ToInt16(temp[0]);
                    currRmName = temp[1];
                    roomList.Add(currRmID, currRmName);
                }
                sr.Close();
                sr.Dispose();
                fs.Close();
                fs.Dispose();
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[initializeRooms]:Exception", ex.Message + (ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message));
                lastErr = "[initializeRooms] " + ex.Message + (ex.InnerException == null ? "" : ":" + ex.InnerException.Message);
                return;
            }

        }

        public Boolean addChargeTxn(int txnID, string desc,int itemID, int qty, int roomID, int bedID, decimal amt, DateTime txnTime)
        {
            try
            {
                if (debug) Log.WriteLog("Invoke Method", "addChargeTxn(" + Convert.ToString(txnID) + "," + desc + "," + Convert.ToString(itemID) + "," +
                       Convert.ToString(roomID) + "," + Convert.ToString(bedID) + "," + String.Format("{0:#,##0.00;-#,##0.00;Zero}", amt) +
                       Convert.ToString(qty) + "," + txnTime.ToString("yyyy-MM-dd HH:mm:ss") + ")");

                if (writeToDB)
                {
                    try
                    {

                        db.OpenConnection();
                        SqlCommand cmd = db.MakeCommand_SP("[dbo].[AddChargeTxn]");
                        cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                        cmd.Parameters["@cardIDM"].Value = cardID;
                        cmd.Parameters.Add(new SqlParameter("@txnID", SqlDbType.Int));
                        cmd.Parameters["@txnID"].Value = txnID;
                        cmd.Parameters.Add(new SqlParameter("@description", SqlDbType.VarChar));
                        cmd.Parameters["@description"].Value = desc;
                        cmd.Parameters.Add(new SqlParameter("@itemID", SqlDbType.Int));
                        cmd.Parameters["@itemID"].Value = itemID;
                        cmd.Parameters.Add(new SqlParameter("@qty", SqlDbType.Int));
                        cmd.Parameters["@qty"].Value = roomID;
                        cmd.Parameters.Add(new SqlParameter("@roomID", SqlDbType.Int));
                        cmd.Parameters["@roomID"].Value = roomID;
                        cmd.Parameters.Add(new SqlParameter("@bedID", SqlDbType.Int));
                        cmd.Parameters["@bedID"].Value = bedID;
                        cmd.Parameters.Add(new SqlParameter("@txnAmt", SqlDbType.Int));
                        cmd.Parameters["@txnAmt"].Value = amt * 100;
                        cmd.Parameters.Add(new SqlParameter("@txnDate", SqlDbType.DateTime));
                        cmd.Parameters["@txnDate"].Value = txnTime;

                        cmd.ExecuteNonQuery();
                    }
                    catch (SqlException ex)
                    {

                        if (debug) Log.WriteLog("Exception", "[addChargeTxn]:SQL Exception", ex.Message + System.Environment.NewLine + (ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message));
                        lastErr = "[addChargeTxn] : Error updating database - " + ex.Message;
                        return false;
                    }
                    finally
                    {
                        if (writeToDB) db.CloseConnection();
                    }
                }

                return true;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[topupDeductPurse]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[topupDeductPurse] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }

        //////////////////////////////////////////////////////////////////////////
        //////////////////////////////////////////////////////////////////////////
        //////////////////////////////////////////////////////////////////////////
        /*For the Printer*/

        private bool initialize4KCardP(string name, DateTime DOB, char gender, short merchantID, int cardNum, short terminalID, string ID, string phone, string email, byte status)
        {
            byte[] blk0 = new byte[16]; byte[] blk1 = new byte[16]; byte[] blk2 = new byte[16]; byte[] data = new byte[16];
            byte[] shortArr = new byte[2]; byte[] intArr = new byte[4]; byte[] lngArr = new byte[8];
            int i = 0;
            Encoding ascii = Encoding.ASCII;
            Byte[] encodedBytes;

            try
            {
                //Block 0
                //Merchant ID
                shortArr = BitConverter.GetBytes(merchantID);
                blk0[0] = shortArr[0];
                blk0[1] = shortArr[1];

                //Initialization Time
                lngArr = BitConverter.GetBytes(DateTime.Now.ToUniversalTime().Ticks);
                blk0[2] = lngArr[0];
                blk0[3] = lngArr[1];
                blk0[4] = lngArr[2];
                blk0[5] = lngArr[3];
                blk0[6] = lngArr[4];
                blk0[7] = lngArr[5];
                blk0[8] = lngArr[6];
                blk0[9] = lngArr[7];

                //Card Number
                intArr = BitConverter.GetBytes(cardNum);
                blk0[10] = intArr[0];
                blk0[11] = intArr[1];
                blk0[12] = intArr[2];
                blk0[13] = intArr[3];

                //Terminal ID
                shortArr = BitConverter.GetBytes(terminalID);
                blk0[14] = shortArr[0];
                blk0[15] = shortArr[1];

                //Block 1 and 8 bytes of block 2
                //Name
                if (name.Length > 24) name = name.Substring(0, 24);
                name = name.PadRight(24, ' ');
                encodedBytes = ascii.GetBytes(name);
                for (i = 0; i < 16; i++)
                {
                    blk1[i] = encodedBytes[i];
                }
                for (i = 0; i < 8; i++)
                {
                    blk2[i] = encodedBytes[i + 16];
                }

                //Block 2 bytes 8-15
                //D.O.B
                shortArr = BitConverter.GetBytes(DOB.Year); //Year (4 digits)
                blk2[8] = shortArr[0];
                blk2[9] = shortArr[1];
                blk2[10] = Convert.ToByte(DOB.Month); //Month
                blk2[11] = Convert.ToByte(DOB.Day); //Day

                //Gender
                blk2[12] = Convert.ToByte(gender);

                //Life cycle +=1, status

                //if (CardComm.AuthBlock(255, KEYA))
                if (CardComm.AuthBlock(255, get_diversify_key(master, CardComm.currCardIDArr, 39)))
                {
                    if (PrinterCardComm.RfidCardRequestAndReadData(242, data, 0))
                    {
                        shortArr = BitConverter.GetBytes((BitConverter.ToInt16(data, 13)) + 1);
                        blk2[13] = shortArr[0];
                        blk2[14] = shortArr[1];
                        blk2[15] = status;
                    }
                    else
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCardP] Error reading block 242" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error reading card data";
                        return false;
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[initialize4kCardP] Error authenticating block 255" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                    this.lastErr = "Authentication Error";
                    return false;
                }

                byte[] emptyBlk = new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

                //Write to Card Sector 39
                //if (CardComm.AuthBlock(255, KEYA))
                if (CardComm.AuthBlock(255, get_diversify_key(master, CardComm.currCardIDArr, 39)))
                {
                    if (!PrinterCardComm.RfidCardRequestAndWriteData(240, blk0, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCardP] Error writing block 240" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!PrinterCardComm.RfidCardRequestAndWriteData(241, blk1, 0))
                    {

                        if (debug) Log.WriteLog("Error", "[initialize4kCardP] Error writing block 241" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!PrinterCardComm.RfidCardRequestAndWriteData(242, blk2, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCardP] Error writing block 242" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card  - " + CardComm.lastErr;
                        return false;
                    }

                    if (!PrinterCardComm.RfidCardRequestAndWriteData(243, emptyBlk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCardP] Error writing block 243\n: " + CardComm.lastErr + "\n");
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!PrinterCardComm.RfidCardRequestAndWriteData(244, emptyBlk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCardP] Error writing block 244\n: " + CardComm.lastErr + "\n");
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!PrinterCardComm.RfidCardRequestAndWriteData(245, emptyBlk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCardP] Error writing block 245\n: " + CardComm.lastErr + "\n");
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!PrinterCardComm.RfidCardRequestAndWriteData(246, emptyBlk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCardP] Error writing block 246\n: " + CardComm.lastErr + "\n");
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!PrinterCardComm.RfidCardRequestAndWriteData(247, emptyBlk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCardP] Error writing block 247\n: " + CardComm.lastErr + "\n");
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!PrinterCardComm.RfidCardRequestAndWriteData(248, emptyBlk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCardP] Error writing block 248\n: " + CardComm.lastErr + "\n");
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!PrinterCardComm.RfidCardRequestAndWriteData(249, emptyBlk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCardP] Error writing block 249\n: " + CardComm.lastErr + "\n");
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!PrinterCardComm.RfidCardRequestAndWriteData(250, emptyBlk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCardP] Error writing block 250\n: " + CardComm.lastErr + "\n");
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!PrinterCardComm.RfidCardRequestAndWriteData(251, emptyBlk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCardP] Error writing block 251\n: " + CardComm.lastErr + "\n");
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    //Update database
                    try
                    {
                        if (debug)
                        {
                            Log.WriteLog("Database", "Executing SP: [dbo].[AddCard] '" + cardID + "'," + cardNum.ToString() + "," + merchantID.ToString() +
                                "," + terminalID.ToString() + ",'" + name + "','" + DOB.ToString("yyyy-MM-dd") + "','" + gender + "','" + ID + "'," + phone + "','" +
                                email + "'", "");
                        }

                        if (writeToDB)
                        {
                            db.OpenConnection();

                            //Register new card
                            SqlCommand cmd = db.MakeCommand_SP("[dbo].[AddCard]");
                            cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                            cmd.Parameters["@cardIDM"].Value = cardID;
                            cmd.Parameters.Add(new SqlParameter("@cardNum", SqlDbType.Int));
                            cmd.Parameters["@cardNum"].Value = cardNum;
                            cmd.Parameters.Add(new SqlParameter("@merchantID", SqlDbType.Int));
                            cmd.Parameters["@merchantID"].Value = merchantID;
                            cmd.Parameters.Add(new SqlParameter("@terminalID", SqlDbType.Int));
                            cmd.Parameters["@terminalID"].Value = terminalID;
                            cmd.Parameters.Add("@name", SqlDbType.VarChar);
                            cmd.Parameters["@name"].Value = name;
                            cmd.Parameters.Add(new SqlParameter("@dob", SqlDbType.SmallDateTime));
                            cmd.Parameters["@dob"].Value = DOB;
                            cmd.Parameters.Add(new SqlParameter("@gender", SqlDbType.Char));
                            cmd.Parameters["@gender"].Value = gender;
                            cmd.Parameters.Add(new SqlParameter("@holderID", SqlDbType.VarChar));
                            cmd.Parameters["@holderID"].Value = ID;
                            cmd.Parameters.Add(new SqlParameter("@phone", SqlDbType.VarChar));
                            cmd.Parameters["@phone"].Value = phone;
                            cmd.Parameters.Add(new SqlParameter("@email", SqlDbType.VarChar));
                            cmd.Parameters["@email"].Value = email;

                            cmd.ExecuteNonQuery();
                        }

                    }
                    catch (SqlException ex)
                    {
                        if (debug) Log.WriteLog("Exception", "SQL Exception in [initialize4kCardP]", ex.Message + System.Environment.NewLine + ex.InnerException == null ? "" : ex.InnerException + System.Environment.NewLine);

                        lastErr = "[initializeCard] : Error updating database - " + ex.Message;
                        return false;
                    }
                    finally
                    {
                        if (writeToDB) db.CloseConnection();
                    }
                    return true;
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[initialize4kCardP]: Authentication Error");

                    this.lastErr = "Authentication Error";
                    return false;
                }

            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[initialize4kCardP]: SQL Exception", ex.Message + ex.InnerException == null ? "" : System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[initialize4kCardP] :" + ex.Message + ex.InnerException == null ? "" : " :" + ex.InnerException.Message;
                return false;
            }
        }

        private bool initialize1KCardP(string name, DateTime DOB, char gender, short merchantID, int cardNum, short terminalID, string ID, string phone, string email, byte status)
        {
            byte[] blk0 = new byte[16]; byte[] blk1 = new byte[16]; byte[] blk2 = new byte[16]; byte[] data = new byte[16];
            byte[] shortArr = new byte[2]; byte[] intArr = new byte[4]; byte[] lngArr = new byte[8];
            int i = 0;
            Encoding ascii = Encoding.ASCII;
            Byte[] encodedBytes;

            try
            {
                //Block 0
                //Merchant ID
                shortArr = BitConverter.GetBytes(merchantID);
                blk0[0] = shortArr[0];
                blk0[1] = shortArr[1];

                //Initialization Time
                lngArr = BitConverter.GetBytes(DateTime.Now.ToUniversalTime().Ticks);
                blk0[2] = lngArr[0];
                blk0[3] = lngArr[1];
                blk0[4] = lngArr[2];
                blk0[5] = lngArr[3];
                blk0[6] = lngArr[4];
                blk0[7] = lngArr[5];
                blk0[8] = lngArr[6];
                blk0[9] = lngArr[7];

                //Card Number
                intArr = BitConverter.GetBytes(cardNum);
                blk0[10] = intArr[0];
                blk0[11] = intArr[1];
                blk0[12] = intArr[2];
                blk0[13] = intArr[3];

                //Terminal ID
                shortArr = BitConverter.GetBytes(terminalID);
                blk0[14] = shortArr[0];
                blk0[15] = shortArr[1];

                //Block 1 and 8 bytes of block 2
                //Name
                if (name.Length > 24) name = name.Substring(0, 24);
                name = name.PadRight(24, ' ');
                encodedBytes = ascii.GetBytes(name);
                for (i = 0; i < 16; i++)
                {
                    blk1[i] = encodedBytes[i];
                }
                for (i = 0; i < 8; i++)
                {
                    blk2[i] = encodedBytes[i + 16];
                }

                //Block 2 bytes 8-15
                //D.O.B
                shortArr = BitConverter.GetBytes(DOB.Year); //Year (4 digits)
                blk2[8] = shortArr[0];
                blk2[9] = shortArr[1];
                blk2[10] = Convert.ToByte(DOB.Month); //Month
                blk2[11] = Convert.ToByte(DOB.Day); //Day

                //Gender
                blk2[12] = Convert.ToByte(gender);

                //Life cycle +=1, status
                if (CardComm.AuthBlock(59, get_diversify_key(master, CardComm.currCardIDArr, 14)))
                {
                    if (PrinterCardComm.RfidCardRequestAndReadData(58, data, 0))
                    {
                        shortArr = BitConverter.GetBytes((BitConverter.ToInt16(data, 13)) + 1);
                        blk2[13] = shortArr[0];
                        blk2[14] = shortArr[1];
                        blk2[15] = status;
                    }
                    else
                    {
                        if (debug) Log.WriteLog("Error", "[initialize1kCardP] Error reading block 58" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error reading card data";
                        return false;
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[initialize1kCardP] Error authenticating block 59" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                    this.lastErr = "Authentication Error";
                    return false;
                }

                byte[] emptyBlk = new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

                //Write to Card Sector 14

                if (CardComm.AuthBlock(59, get_diversify_key(master, CardComm.currCardIDArr, 14)))
                {
                    if (!PrinterCardComm.RfidCardRequestAndWriteData(56, blk0, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize1kCardP] Error writing block 56" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!PrinterCardComm.RfidCardRequestAndWriteData(57, blk1, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize1kCardP] Error writing block 57" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!PrinterCardComm.RfidCardRequestAndWriteData(58, blk2, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize1kCardP] Error writing block 58" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card  - " + CardComm.lastErr;
                        return false;
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[initialize1kCardP] Error authenticating block 59" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                    this.lastErr = "Authentication Error";
                    return false;
                }

                //Write to card sector 15
                if (CardComm.AuthBlock(63, get_diversify_key(master, CardComm.currCardIDArr, 15)))
                {
                    if (!PrinterCardComm.RfidCardRequestAndWriteData(60, emptyBlk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize1kCardP] Error writing block 60" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!PrinterCardComm.RfidCardRequestAndWriteData(61, emptyBlk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize1kCardP] Error writing block 61" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!PrinterCardComm.RfidCardRequestAndWriteData(62, emptyBlk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[initialize1kCardP] Error writing block 62" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    //Update database
                    try
                    {
                        if (debug)
                        {
                            Log.WriteLog("Database", "Executing SP: [dbo].[AddCard] '" + cardID + "'," + cardNum.ToString() + "," + merchantID.ToString() +
                                "," + terminalID.ToString() + ",'" + name + "','" + DOB.ToString("yyyy-MM-dd") + "','" + gender + "','" + ID + "'," + phone + "','" +
                                email + "'", "" + System.Environment.NewLine);
                        }
                        if (writeToDB)
                        {
                            db.OpenConnection();

                            //Register new card
                            SqlCommand cmd = db.MakeCommand_SP("[dbo].[AddCard]");
                            cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                            cmd.Parameters["@cardIDM"].Value = cardID;
                            cmd.Parameters.Add(new SqlParameter("@cardNum", SqlDbType.Int));
                            cmd.Parameters["@cardNum"].Value = cardNum;
                            cmd.Parameters.Add(new SqlParameter("@merchantID", SqlDbType.Int));
                            cmd.Parameters["@merchantID"].Value = merchantID;
                            cmd.Parameters.Add(new SqlParameter("@terminalID", SqlDbType.Int));
                            cmd.Parameters["@terminalID"].Value = terminalID;
                            cmd.Parameters.Add("@name", SqlDbType.VarChar);
                            cmd.Parameters["@name"].Value = name;
                            cmd.Parameters.Add(new SqlParameter("@dob", SqlDbType.SmallDateTime));
                            cmd.Parameters["@dob"].Value = DOB;
                            cmd.Parameters.Add(new SqlParameter("@gender", SqlDbType.Char));
                            cmd.Parameters["@gender"].Value = gender;
                            cmd.Parameters.Add(new SqlParameter("@holderID", SqlDbType.VarChar));
                            cmd.Parameters["@holderID"].Value = ID;
                            cmd.Parameters.Add(new SqlParameter("@phone", SqlDbType.VarChar));
                            cmd.Parameters["@phone"].Value = phone;
                            cmd.Parameters.Add(new SqlParameter("@email", SqlDbType.VarChar));
                            cmd.Parameters["@email"].Value = email;

                            cmd.ExecuteNonQuery();
                        }

                    }
                    catch (SqlException ex)
                    {
                        if (debug) Log.WriteLog("Exception", "SQL Exception in [initialize1kCardP]", ex.Message + System.Environment.NewLine + ex.InnerException + System.Environment.NewLine);
                        lastErr = "[initialize1kCardP] : Error updating database - " + ex.Message;
                        return false;
                    }
                    finally
                    {
                        if (writeToDB) db.CloseConnection();
                    }

                    return true;
                }

                else
                {
                    if (debug) Log.WriteLog("Error", "[initialize1kCardP] Error authenticating block 63" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                    this.lastErr = "Authentication Error";
                    return false;
                }
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[initialize1kCardP]: Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message + System.Environment.NewLine);
                lastErr = "[initialize1kCardP] :" + ex.Message + ex.InnerException == null ? "" : " :" + ex.InnerException.Message;
                return false;
            }

        }

        /// <summary>
        /// Update card holder details
        /// </summary>
        /// <param name="name">Name of card holder</param>
        /// <param name="DOB">Date of Birth</param>
        /// <param name="gender">Gender - M: Male and F: Female</param>
        /// <param name="cardNum">Unique card number to identify this card</param>
        /// <param name="merchantID">Unique ID to identify the merchant</param>
        /// <param name="expiry">Expiry date of the card</param>
        /// <param name="ID">ID of the card holder</param>
        /// <param name="phone">Phone number of the card holder</param>
        /// <param name="email">Email address of the card holder</param>
        /// <param name="memID">Member ID of the card holder</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        public bool updateCardP(string name, DateTime DOB, char gender, int cardNum, short merchantID, DateTime expiry, string ID, string phone, string email, string memID)
        {
            if (_cardType == MIFARE_1K) return update1kCardP(name, DOB, gender, cardNum, merchantID, expiry, ID, phone, email, memID);
            else if (_cardType == MIFARE_4K) return update4kCardP(name, DOB, gender, cardNum, merchantID, expiry, ID, phone, email, memID);
            else return false;
        }

        /// <summary>
        /// Update the status of the card
        /// </summary>
        /// <param name="status">The new status</param>
        /// <returns></returns>
        public bool updateCardStatusP(byte status)
        {
            if (_cardType == MIFARE_1K) return update1kCardStatusP(status);
            else if (_cardType == MIFARE_4K) return update4kCardStatusP(status);
            else return false;
        }

        private bool update1kCardStatusP(byte status)
        {
            byte[] blk = new byte[16];

            if (debug) Log.WriteLog("Invoke Method", "update1kCardStatusP(" + status.ToString() + ")" + System.Environment.NewLine);

            try
            {
                if (!readCardP()) return false;
                if (CardComm.currCardID == String.Empty)
                {
                    if (debug) Log.WriteLog("Error", "[update1kCardStatusP]: No Card Detected");
                    lastErr = "No Card Detected";
                    return false;
                }

                if (CardComm.AuthBlock(59, get_diversify_key(master, CardComm.currCardIDArr, 14)) == false)
                {
                    if (debug) Log.WriteLog("Error", "[update1kCardStatusP]: Authentication Failed For Block 255" + System.Environment.NewLine + CardComm.lastErr);
                    lastErr = "Authentication Failed - " + CardComm.lastErr;
                    return false;
                }

                if (PrinterCardComm.RfidCardRequestAndReadData(58, blk, 0))
                {
                    blk[15] = status;
                    if (!PrinterCardComm.RfidCardRequestAndWriteData(58, blk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[update1kCardStatusP]: Error Writing to block 242" + System.Environment.NewLine + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[update1kCardStatusP] Error reading block 58" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                    this.lastErr = "Error reading card data";
                    return false;
                }
                return true;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[update1kCardStatusP]: Exception", ex.Message);
                lastErr = "[update1kCardStatusP]: " + ex.Message;
                return false;
            }
        }

        private bool update4kCardStatusP(byte status)
        {
            byte[] blk = new byte[16];

            if (debug) Log.WriteLog("Invoke Method", "update4kCardStatusP(" + status.ToString() + ")" + System.Environment.NewLine);

            try
            {
                if (!readCardP()) return false;
                if (CardComm.currCardID == String.Empty)
                {
                    if (debug) Log.WriteLog("Error", "[update4kCardStatusP]: No Card Detected");
                    lastErr = "No Card Detected";
                    return false;
                }

                if (!CardComm.AuthBlock(255, get_diversify_key(master, CardComm.currCardIDArr, 39)))
                {
                    if (debug) Log.WriteLog("Error", "[update1kCardStatusP]: Authentication Failed For Block 255" + System.Environment.NewLine + CardComm.lastErr);
                    lastErr = "Authentication Failed - " + CardComm.lastErr;
                    return false;
                }

                if (PrinterCardComm.RfidCardRequestAndReadData(242, blk, 0))
                {
                    blk[15] = status;
                    if (!PrinterCardComm.RfidCardRequestAndWriteData(242, blk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[update4kCardStatusP]: Error Writing to block 242" + System.Environment.NewLine + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[update4kCardStatusP] Error reading block 58" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                    this.lastErr = "Error reading card data";
                    return false;
                }
                return true;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[update4kCardStatusP]: Exception", ex.Message);
                lastErr = "[update4kCardStatusP]: " + ex.Message;
                return false;
            }

        }

        private bool update1kCardP(string name, DateTime DOB, char gender, int cardNum, short merchantID, DateTime expiry, string ID, string phone, string email, string memID)
        {
            byte[] blk0 = new byte[16]; byte[] blk1 = new byte[16]; byte[] blk2 = new byte[16]; byte[] blk3 = new byte[16];
            byte[] shortArr = new byte[2]; byte[] intArr = new byte[4]; byte[] lngArr = new byte[8];
            int i = 0;
            Encoding ascii = Encoding.ASCII;
            Byte[] encodedBytes;

            if (debug) Log.WriteLog("Invoke Method", "update1kCardP(" + name + "," + DOB.ToString("yyyy-MM-dd") +
                    "," + gender + "," + Convert.ToString(cardNum) + "," + Convert.ToString(merchantID) + "," +
                    expiry.ToString("yyyy-MM-dd") + "," + Convert.ToString(ID) + "," + phone + "," + email + "," + memID + ")" + System.Environment.NewLine);

            try
            {
                if (!readCardP()) return false;
                if (CardComm.currCardID == String.Empty)
                {
                    if (debug) Log.WriteLog("Error", "[update1kCardP]: No Card Detected");
                    lastErr = "No Card Detected";
                    return false;
                }

                if (CardComm.AuthBlock(59, get_diversify_key(master, CardComm.currCardIDArr, 14)) == false)
                {
                    if (debug) Log.WriteLog("Error", "[update1kCardP]: Authentication Failed For Block 255" + System.Environment.NewLine + CardComm.lastErr);
                    lastErr = "Authentication Failed - " + CardComm.lastErr;
                    return false;
                }

                //Read Block 0: Issue Merchant, Issue Date, Card Number, Terminal ID
                if (PrinterCardComm.RfidCardRequestAndReadData(56, blk0, 0))
                {
                    //Card Number
                    intArr = BitConverter.GetBytes(cardNum);
                    blk0[10] = intArr[0];
                    blk0[11] = intArr[1];
                    blk0[12] = intArr[2];
                    blk0[13] = intArr[3];

                    //Read Life Cycle
                    if (PrinterCardComm.RfidCardRequestAndReadData(58, blk2, 0))
                    {
                        shortArr = BitConverter.GetBytes(BitConverter.ToInt16(blk2, 13));
                        blk2[13] = shortArr[0];
                        blk2[14] = shortArr[1];
                    }
                    else
                    {
                        if (debug) Log.WriteLog("Error", "[update1kCardP] Error reading block 58" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error reading card data";
                        return false;
                    }

                    //Block 1 and 8 bytes of block 2
                    //Name
                    if (name.Length > 24) name = name.Substring(0, 24);
                    name = name.PadRight(24, ' ');
                    encodedBytes = ascii.GetBytes(name);
                    for (i = 0; i < 16; i++)
                    {
                        blk1[i] = encodedBytes[i];
                    }
                    for (i = 0; i < 8; i++)
                    {
                        blk2[i] = encodedBytes[i + 16];
                    }

                    //Block 2 bytes 8-15
                    //D.O.B
                    shortArr = BitConverter.GetBytes(DOB.Year); //Year (4 digits)
                    blk2[8] = shortArr[0];
                    blk2[9] = shortArr[1];
                    blk2[10] = Convert.ToByte(DOB.Month); //Month
                    blk2[11] = Convert.ToByte(DOB.Day); //Day

                    //Gender
                    blk2[12] = Convert.ToByte(gender);

                    if (!PrinterCardComm.RfidCardRequestAndWriteData(56, blk0, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[update1kCardP]: Error Writing to block 240" + System.Environment.NewLine + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!PrinterCardComm.RfidCardRequestAndWriteData(57, blk1, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[update1kCardP]: Error Writing to block 241" + System.Environment.NewLine + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!PrinterCardComm.RfidCardRequestAndWriteData(58, blk2, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[update1kCardP]: Error Writing to block 242" + System.Environment.NewLine + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    //Update purse expiry and member ID
                    short sector = getProgramSector(merchantID, 4);
                    if (!editEPurseP(Convert.ToInt16(sector), 0, expiry, merchantID, memID))
                        return false;

                    //update loyalty points expiry and member ID
                    short order = 0;
                    foreach (KeyValuePair<DirData, short> item in merchantDict)
                    {
                        if (item.Key.programType >> 4 == 2)
                        {
                            order = Convert.ToInt16((item.Key.programType & 15));
                            sector = getProgramSector(merchantID, 2, order);
                            if (!editLoyaltyP(Convert.ToInt16(sector), order, 0, expiry, merchantID, memID))
                                return false;
                        }
                    }
                    //sector = getProgramSector(merchantID, 2);
                    //if (!editLoyaltyP(Convert.ToInt16(sector), 0, expiry, merchantID, memID))
                    //    return false;

                    //update database
                    try
                    {
                        if (debug)
                        {
                            Log.WriteLog("Database", "Executing SP: [dbo].[UpdateCardP] '" + cardID + "'," + cardNum.ToString() + "," + merchantID.ToString() +
                                ",'" + name + "','" + DOB.ToString("yyyy-MM-dd") + "','" + gender + "','" + ID + "'," + phone + "','" +
                                email + "','" + memID + "'", "");
                        }

                        if (writeToDB)
                        {

                            db.OpenConnection();
                            SqlCommand cmd = db.MakeCommand_SP("[dbo].[UpdateCardP]");
                            cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                            cmd.Parameters["@cardIDM"].Value = cardID;
                            cmd.Parameters.Add(new SqlParameter("@cardNum", SqlDbType.Int));
                            cmd.Parameters["@cardNum"].Value = cardNum;
                            cmd.Parameters.Add(new SqlParameter("@merchantID", SqlDbType.Int));
                            cmd.Parameters["@merchantID"].Value = merchantID;
                            cmd.Parameters.Add(new SqlParameter("@name", SqlDbType.VarChar));
                            cmd.Parameters["@name"].Value = name;
                            cmd.Parameters.Add(new SqlParameter("@DOB", SqlDbType.SmallDateTime));
                            cmd.Parameters["@DOB"].Value = DOB;
                            cmd.Parameters.Add(new SqlParameter("@gender", SqlDbType.Char));
                            cmd.Parameters["@gender"].Value = gender;
                            cmd.Parameters.Add(new SqlParameter("@holderID", SqlDbType.Char));
                            cmd.Parameters["@holderID"].Value = ID;
                            cmd.Parameters.Add(new SqlParameter("@phone", SqlDbType.Char));
                            cmd.Parameters["@phone"].Value = phone;
                            cmd.Parameters.Add(new SqlParameter("@email", SqlDbType.Char));
                            cmd.Parameters["@email"].Value = email;
                            cmd.Parameters.Add(new SqlParameter("@memID", SqlDbType.Char));
                            cmd.Parameters["@memID"].Value = memID;

                            cmd.ExecuteNonQuery();
                        }
                        return true;
                    }
                    catch (SqlException ex)
                    {
                        if (debug) Log.WriteLog("Exception", "[update1kCardP]:SQL Exception", ex.Message + (ex.InnerException == null ? "" : System.Environment.NewLine + ex.InnerException.Message));
                        lastErr = "[update1kCardP] : Error updating database - " + ex.Message;
                        return false;
                    }
                    finally
                    {
                        if (writeToDB) db.CloseConnection();
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[update1kCardP]: Error Reading Block 56" + System.Environment.NewLine + CardComm.lastErr + System.Environment.NewLine);
                    lastErr = "Error Reading Card - " + CardComm.lastErr;
                    return false;
                }
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[update1kCardP]: Exception", ex.Message + (ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message));
                lastErr = "[updateCardP]: " + ex.Message + (ex.InnerException == null ? "" : ":" + ex.InnerException.Message);
                return false;
            }

        }

        private bool update4kCardP(string name, DateTime DOB, char gender, int cardNum, short merchantID, DateTime expiry, string ID, string phone, string email, string memID)
        {
            byte[] blk0 = new byte[16]; byte[] blk1 = new byte[16]; byte[] blk2 = new byte[16]; byte[] blk3 = new byte[16];
            byte[] shortArr = new byte[2]; byte[] intArr = new byte[4]; byte[] lngArr = new byte[8];
            int i = 0;
            Encoding ascii = Encoding.ASCII;
            Byte[] encodedBytes;

            if (debug) Log.WriteLog("Invoke Method", "update4kCardP(" + name + "," + DOB.ToString("yyyy-MM-dd") +
                    "," + gender + "," + Convert.ToString(cardNum) + "," + Convert.ToString(merchantID) + "," +
                    expiry.ToString("yyyy-MM-dd") + "," + Convert.ToString(ID) + "," + phone + "," + email + "," + memID + ")" + System.Environment.NewLine);

            try
            {
                if (CardComm.currCardID == String.Empty)
                {
                    if (debug) Log.WriteLog("Error", "[update4kCardP]: No Card Detected");
                    lastErr = "No Card Detected";
                    return false;
                }

                //if (CardComm.AuthBlock(255, KEYA))
                if (!CardComm.AuthBlock(255, get_diversify_key(master, CardComm.currCardIDArr, 39)))
                {
                    if (debug) Log.WriteLog("Error", "[update4kCardP]: Authentication Failed For Block 255" + System.Environment.NewLine + CardComm.lastErr);
                    lastErr = "Authentication Failed - " + CardComm.lastErr;
                    return false;
                }

                //Read Block 0: Issue Merchant, Issue Date, Card Number, Terminal ID
                if (PrinterCardComm.RfidCardRequestAndReadData(240, blk0, 0))
                {
                    //Card Number
                    intArr = BitConverter.GetBytes(cardNum);
                    blk0[10] = intArr[0];
                    blk0[11] = intArr[1];
                    blk0[12] = intArr[2];
                    blk0[13] = intArr[3];

                    //Life cycle
                    if (PrinterCardComm.RfidCardRequestAndReadData(242, blk2, 0))
                    {
                        shortArr = BitConverter.GetBytes(BitConverter.ToInt16(blk2, 13));
                        blk2[13] = shortArr[0];
                        blk2[14] = shortArr[1];
                    }
                    else
                    {
                        if (debug) Log.WriteLog("Error", "[initialize4kCardP] Error reading block 242" + System.Environment.NewLine + ": " + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error reading card data";
                        return false;
                    }

                    //Block 1 and 8 bytes of block 2
                    //Name
                    if (name.Length > 24) name = name.Substring(0, 24);
                    name = name.PadRight(24, ' ');
                    encodedBytes = ascii.GetBytes(name);
                    for (i = 0; i < 16; i++)
                    {
                        blk1[i] = encodedBytes[i];
                    }
                    for (i = 0; i < 8; i++)
                    {
                        blk2[i] = encodedBytes[i + 16];
                    }

                    //Block 2 bytes 8-15
                    //D.O.B
                    shortArr = BitConverter.GetBytes(DOB.Year); //Year (4 digits)
                    blk2[8] = shortArr[0];
                    blk2[9] = shortArr[1];
                    blk2[10] = Convert.ToByte(DOB.Month); //Month
                    blk2[11] = Convert.ToByte(DOB.Day); //Day

                    //Gender
                    blk2[12] = Convert.ToByte(gender);

                    if (!PrinterCardComm.RfidCardRequestAndWriteData(240, blk0, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[update4kCardP]: Error Writing to block 240" + System.Environment.NewLine + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!PrinterCardComm.RfidCardRequestAndWriteData(241, blk1, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[update4kCardP]: Error Writing to block 241" + System.Environment.NewLine + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    if (!PrinterCardComm.RfidCardRequestAndWriteData(242, blk2, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[update4kCardP]: Error Writing to block 242" + System.Environment.NewLine + CardComm.lastErr + System.Environment.NewLine);
                        this.lastErr = "Error Writing to Card - " + CardComm.lastErr;
                        return false;
                    }

                    //Update purse expiry and member ID
                    short sector = getProgramSector(merchantID, 4);
                    if (!editEPurseP(Convert.ToInt16(sector), 0, expiry, merchantID, memID))
                        return false;

                    //update loyalty points expiry and member ID
                    short order = 0;
                    foreach (KeyValuePair<DirData, short> item in merchantDict)
                    {
                        if (item.Value >> 4 == 2)
                        {
                            order = Convert.ToInt16((item.Value & 15));
                            if (!editLoyaltyP(Convert.ToInt16(sector), order, 0, expiry, merchantID, memID))
                                return false;
                        }
                    }
                    //sector = getProgramSector(merchantID, 2);
                    //if (!editLoyaltyP(Convert.ToInt16(sector), 0, expiry, merchantID, memID))
                    //    return false;

                    //update database
                    try
                    {
                        if (debug)
                        {
                            Log.WriteLog("Database", "Executing SP: [dbo].[UpdateCardP] '" + cardID + "'," + cardNum.ToString() + "," + merchantID.ToString() +
                                ",'" + name + "','" + DOB.ToString("yyyy-MM-dd") + "','" + gender + "','" + ID + "'," + phone + "','" +
                                email + "','" + memID + "'", "");
                        }

                        if (writeToDB)
                        {

                            db.OpenConnection();
                            SqlCommand cmd = db.MakeCommand_SP("[dbo].[UpdateCardP]");
                            cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                            cmd.Parameters["@cardIDM"].Value = cardID;
                            cmd.Parameters.Add(new SqlParameter("@cardNum", SqlDbType.Int));
                            cmd.Parameters["@cardNum"].Value = cardNum;
                            cmd.Parameters.Add(new SqlParameter("@merchantID", SqlDbType.Int));
                            cmd.Parameters["@merchantID"].Value = merchantID;
                            cmd.Parameters.Add(new SqlParameter("@name", SqlDbType.VarChar));
                            cmd.Parameters["@name"].Value = name;
                            cmd.Parameters.Add(new SqlParameter("@DOB", SqlDbType.SmallDateTime));
                            cmd.Parameters["@DOB"].Value = DOB;
                            cmd.Parameters.Add(new SqlParameter("@gender", SqlDbType.Char));
                            cmd.Parameters["@gender"].Value = gender;
                            cmd.Parameters.Add(new SqlParameter("@holderID", SqlDbType.Char));
                            cmd.Parameters["@holderID"].Value = ID;
                            cmd.Parameters.Add(new SqlParameter("@phone", SqlDbType.Char));
                            cmd.Parameters["@phone"].Value = phone;
                            cmd.Parameters.Add(new SqlParameter("@email", SqlDbType.Char));
                            cmd.Parameters["@email"].Value = email;
                            cmd.Parameters.Add(new SqlParameter("@memID", SqlDbType.Char));
                            cmd.Parameters["@memID"].Value = email;

                            cmd.ExecuteNonQuery();
                        }
                        return true;
                    }
                    catch (SqlException ex)
                    {
                        if (debug) Log.WriteLog("Exception", "[update4kCardP]:SQL Exception", ex.Message + (ex.InnerException == null ? "" : System.Environment.NewLine + ex.InnerException.Message));
                        lastErr = "[update4kCardP] : Error updating database - " + ex.Message;
                        return false;
                    }
                    finally
                    {
                        if (writeToDB) db.CloseConnection();
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[update4kCardP]: Error Reading Block 240" + System.Environment.NewLine + CardComm.lastErr + System.Environment.NewLine);
                    lastErr = "Error Reading Card - " + CardComm.lastErr;
                    return false;
                }
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[update4kCardP]: Exception", ex.Message + (ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message));
                lastErr = "[update4kCardP]: " + ex.Message + (ex.InnerException == null ? "" : ":" + ex.InnerException.Message);
                return false;
            }

        }

        /// <summary>
        /// Reads basic card details (Name, D.O.B, Expiry Date, Card Number, Gender) only
        /// </summary>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        private bool readCardP()
        {
            if (_cardType == MIFARE_1K)
            {
                CardComm.DisconnectCard();
                return read1KCardP();
            }
            else if (_cardType == MIFARE_4K)
            {
                CardComm.DisconnectCard();
                return read4kCardP();
            }
            else return false;
        }

        private bool read4kCardP()
        {
            //Read info from block 39
            byte[] data = new byte[16];

            try
            {
                if (debug) Log.WriteLog("Invoke Method", "read4kCardP()");
                if (CardComm.currCardID == String.Empty)
                {
                    if (debug) Log.WriteLog("Error", "[read4kCardP]: No Card Detected");
                    lastErr = "No Card Detected!";
                    return false;
                }

                //if (CardComm.AuthBlock(255, KEYA))
                if (!CardComm.AuthBlock(255, get_diversify_key(master, CardComm.currCardIDArr, 39)))
                {
                    if (debug) Log.WriteLog("Error", "[read4kCardP]: Authentication Failed for Block 255");
                    lastErr = "Authentication Failed  - " + CardComm.lastErr;
                    return false;
                }

                //Read Block 0: Issue Merchant, Issue Date, Card Number, Terminal ID
                if (PrinterCardComm.RfidCardRequestAndReadData(240, data, 0))
                {
                    _issueMerchantID = BitConverter.ToInt16(data, 0);
                    _issueDateTime = new DateTime(BitConverter.ToInt64(data, 2), DateTimeKind.Utc).ToLocalTime();
                    _issueCardID = BitConverter.ToInt32(data, 10);
                    _issueTerminalID = BitConverter.ToInt16(data, 14);
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read4kCardP]: Error reading block 240" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read Block 1: Name 1
                if (PrinterCardComm.RfidCardRequestAndReadData(241, data, 0))
                {
                    _cardHolderName = ASCIIEncoding.ASCII.GetString(data, 0, 16);
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read4kCardP]: Error reading block 241" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read Block 2: Name 2, D.O.B, Gender, Status
                if (PrinterCardComm.RfidCardRequestAndReadData(242, data, 0))
                {
                    _cardHolderName = _cardHolderName + ASCIIEncoding.ASCII.GetString(data, 0, 8);
                    short year = BitConverter.ToInt16(data, 8);
                    byte mth = data[10];
                    byte day = data[11];
                    if (year == 0 || mth == 0 || year == 0)
                    {
                        _cardHolderDOB = new DateTime(1, 1, 1);
                    }
                    else _cardHolderDOB = new DateTime(BitConverter.ToInt16(data, 8), data[10], data[11]).Date;
                    _cardHolderGender = Convert.ToChar(ASCIIEncoding.ASCII.GetString(data, 12, 1));
                    _cardStatus = data[15];
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read4kCardP]: Error reading block 242" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                _cardHolderName = _cardHolderName.Trim();


                //Read Block 3: Sectors Used
                if (PrinterCardComm.RfidCardRequestAndReadData(243, data, 0))
                {
                    sectorsUsed = BitConverter.ToInt64(data, 0);
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read4kCardP]: Error reading block 243" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read Block 4: Sector 1-5 info
                if (PrinterCardComm.RfidCardRequestAndReadData(244, data, 0))
                {
                    DirData currDir;
                    for (int i = 0; i < 4; i++)
                    {
                        currDir.merchantID = BitConverter.ToInt16(data, i * 3);
                        currDir.programType = data[i * 3 + 2];
                        if (currDir.merchantID != 0 && merchantDict.ContainsKey(currDir) == false) merchantDict.Add(currDir, Convert.ToInt16(i));
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read4kCardP]: Error reading block 244" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read Block 5: Sector 6-10 info
                if (PrinterCardComm.RfidCardRequestAndReadData(245, data, 0))
                {
                    DirData currDir;
                    for (int i = 0; i < 5; i++)
                    {
                        currDir.merchantID = BitConverter.ToInt16(data, i * 3);
                        currDir.programType = data[i * 3 + 2];
                        if (currDir.merchantID != 0 && merchantDict.ContainsKey(currDir) == false) merchantDict.Add(currDir, Convert.ToInt16(i + 6));
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read4kCardP]: Error reading block 245" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read Block 6: Sector 11-15 info
                if (PrinterCardComm.RfidCardRequestAndReadData(246, data, 0))
                {
                    DirData currDir;
                    for (int i = 0; i < 5; i++)
                    {
                        currDir.merchantID = BitConverter.ToInt16(data, i * 3);
                        currDir.programType = data[i * 3 + 2];
                        if (currDir.merchantID != 0 && merchantDict.ContainsKey(currDir) == false) merchantDict.Add(currDir, Convert.ToInt16(i + 11));
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read4kCardP]: Error reading block 246" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read Block 7: Sector 16-20 info
                if (PrinterCardComm.RfidCardRequestAndReadData(247, data, 0))
                {
                    DirData currDir;
                    for (int i = 0; i < 5; i++)
                    {
                        currDir.merchantID = BitConverter.ToInt16(data, i * 3);
                        currDir.programType = data[i * 3 + 2];
                        if (currDir.merchantID != 0 && merchantDict.ContainsKey(currDir) == false) merchantDict.Add(currDir, Convert.ToInt16(i + 16));
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read4kCardP]: Error reading block 247" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read Block 8: Sector 21-25 info
                if (PrinterCardComm.RfidCardRequestAndReadData(248, data, 0))
                {
                    DirData currDir;
                    for (int i = 0; i < 5; i++)
                    {
                        currDir.merchantID = BitConverter.ToInt16(data, i * 3);
                        currDir.programType = data[i * 3 + 2];
                        if (currDir.merchantID != 0 && merchantDict.ContainsKey(currDir) == false) merchantDict.Add(currDir, Convert.ToInt16(i + 21));
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read4kCardP]: Error reading block 248" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read Block 9: Sector 26-30 info
                if (PrinterCardComm.RfidCardRequestAndReadData(249, data, 0))
                {
                    DirData currDir;
                    for (int i = 0; i < 5; i++)
                    {
                        currDir.merchantID = BitConverter.ToInt16(data, i * 3);
                        currDir.programType = data[i * 3 + 2];
                        if (currDir.merchantID != 0 && merchantDict.ContainsKey(currDir) == false) merchantDict.Add(currDir, Convert.ToInt16(i + 26));
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read4kCardP]: Error reading block 249" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read Block 10: Sector 31-35 info
                if (PrinterCardComm.RfidCardRequestAndReadData(250, data, 0))
                {
                    DirData currDir;
                    for (int i = 0; i < 5; i++)
                    {
                        currDir.merchantID = BitConverter.ToInt16(data, i * 3);
                        currDir.programType = data[i * 3 + 2];
                        if (currDir.merchantID != 0 && merchantDict.ContainsKey(currDir) == false) merchantDict.Add(currDir, Convert.ToInt16(i + 31));
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read4kCardP]: Error reading block 250" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read from database
                try
                {
                    if (debug)
                    {
                        Log.WriteLog("Database", "Executing SP: [dbo].[GetCardHolderInfo] '" + cardID + "'");
                    }
                    if (writeToDB)
                    {
                        DataSet ds = new DataSet();
                        SqlDataAdapter da = new SqlDataAdapter();
                        SqlCommand cmd = db.MakeCommand_SP("dbo.GetCardHolderInfo");
                        cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                        cmd.Parameters["@cardIDM"].Value = cardID;
                        da.SelectCommand = cmd;
                        da.Fill(ds);

                        _cardHolderID = ds.Tables[0].Rows[0]["HolderID"].ToString();
                        _cardHolderPhone = ds.Tables[0].Rows[0]["Phone"].ToString();
                        _cardHolderEmail = ds.Tables[0].Rows[0]["Email"].ToString();

                        da.Dispose();
                        ds.Dispose();
                    }

                }
                catch (SqlException ex)
                {
                    if (debug) Log.WriteLog("Exception", "[read4kCardP]:SQL Exception", ex.Message + "" + System.Environment.NewLine + ex.InnerException);

                    lastErr = "[read4kCardP] : Error reading from database - " + ex.Message;
                    return false;
                }
                finally
                {
                    if (writeToDB) db.CloseConnection();
                }

                return true;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[read4kCardP]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[read4kCardP] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }

        private bool read1KCardP()
        {
            //Read info from sector 14 & 15
            byte[] data = new byte[16];

            try
            {
                if (debug) Log.WriteLog("Invoke Method", "read1kCardP()");
                if (CardComm.currCardID == String.Empty)
                {
                    if (debug) Log.WriteLog("Error", "[read1kCardP]: No Card Detected");
                    lastErr = "No Card Detected!";
                    return false;
                }

                //Authenticate sector 14
                if (CardComm.AuthBlock(59, get_diversify_key(master, CardComm.currCardIDArr, 14)) == false)
                {
                    if (debug) Log.WriteLog("Error", "[read1kCardP]: Authentication Failed for Block 59");
                    lastErr = "Authentication Failed  - " + CardComm.lastErr;
                    return false;
                }

                //Read Block 0: Issue Merchant, Issue Date, Card Number, Terminal ID
                if (PrinterCardComm.RfidCardRequestAndReadData(56, data, 0))
                {
                    _issueMerchantID = BitConverter.ToInt16(data, 0);
                    _issueDateTime = new DateTime(BitConverter.ToInt64(data, 2), DateTimeKind.Utc).ToLocalTime();
                    _issueCardID = BitConverter.ToInt32(data, 10);
                    _issueTerminalID = BitConverter.ToInt16(data, 14);
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read1kCardP]: Error reading block 56" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read Block 1: Name 1
                if (PrinterCardComm.RfidCardRequestAndReadData(57, data, 0))
                {
                    _cardHolderName = ASCIIEncoding.ASCII.GetString(data, 0, 16);
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read1kCardP]: Error reading block 57" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read Block 2: Name 2 (1st 8 bytes),D.O.B, Gender
                if (PrinterCardComm.RfidCardRequestAndReadData(58, data, 0))
                {
                    _cardHolderName = _cardHolderName + ASCIIEncoding.ASCII.GetString(data, 0, 8);
                    short year = BitConverter.ToInt16(data, 8);
                    byte mth = data[10];
                    byte day = data[11];
                    if (year == 0 || mth == 0 || day == 0)
                        _cardHolderDOB = new DateTime(1, 1, 1);
                    else
                        _cardHolderDOB = new DateTime(BitConverter.ToInt16(data, 8), data[10], data[11]).Date;
                    _cardHolderGender = Convert.ToChar(ASCIIEncoding.ASCII.GetString(data, 12, 1));
                    _cardStatus = data[15];
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read1kCardP]: Error reading block 58" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                _cardHolderName = _cardHolderName.Trim();


                //Authenticate sector 15
                if (CardComm.AuthBlock(63, get_diversify_key(master, CardComm.currCardIDArr, 15)) == false)
                {
                    if (debug) Log.WriteLog("Error", "[read1kCardP]: Authentication Failed for Block 63");
                    lastErr = "Authentication Failed  - " + CardComm.lastErr;
                    return false;
                }

                //Read Block 1: Sectors Used
                if (PrinterCardComm.RfidCardRequestAndReadData(60, data, 0))
                {
                    sectorsUsed = BitConverter.ToInt64(data, 0);
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read1kCardP]: Error reading block 60" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read Block 2: Sector 1-5 info
                if (PrinterCardComm.RfidCardRequestAndReadData(61, data, 0))
                {
                    DirData currDir;
                    for (int i = 0; i < 5; i++)
                    {
                        currDir.merchantID = BitConverter.ToInt16(data, i * 3);
                        currDir.programType = data[i * 3 + 2];
                        if (currDir.merchantID != 0 && merchantDict.ContainsKey(currDir) == false) merchantDict.Add(currDir, Convert.ToInt16(i));
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read1kCardP]: Error reading block 61" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read Block 3: Sector 6-10 info
                if (PrinterCardComm.RfidCardRequestAndReadData(62, data, 0))
                {
                    DirData currDir;
                    for (int i = 0; i < 5; i++)
                    {
                        currDir.merchantID = BitConverter.ToInt16(data, i * 3);
                        currDir.programType = data[i * 3 + 2];
                        if (currDir.merchantID != 0 && merchantDict.ContainsKey(currDir) == false) merchantDict.Add(currDir, Convert.ToInt16(i + 5));
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[read1kCardP]: Error reading block 62" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                //Read from database
                try
                {
                    if (debug)
                    {
                        Log.WriteLog("Database", "Executing SP: [dbo].[GetCardHolderInfo] '" + cardID + "'");
                    }
                    if (writeToDB)
                    {
                        DataSet ds = new DataSet();
                        SqlDataAdapter da = new SqlDataAdapter();
                        SqlCommand cmd = db.MakeCommand_SP("dbo.GetCardHolderInfo");
                        cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                        cmd.Parameters["@cardIDM"].Value = cardID;
                        da.SelectCommand = cmd;
                        da.Fill(ds);

                        _cardHolderID = ds.Tables[0].Rows[0]["HolderID"].ToString();
                        _cardHolderPhone = ds.Tables[0].Rows[0]["Phone"].ToString();
                        _cardHolderEmail = ds.Tables[0].Rows[0]["Email"].ToString();

                        da.Dispose();
                        ds.Dispose();
                    }

                }
                catch (SqlException ex)
                {
                    if (debug) Log.WriteLog("Exception", "[read1kCardP]:SQL Exception", ex.Message + "" + System.Environment.NewLine + ex.InnerException);

                    lastErr = "[read4kCardP] : Error reading from database - " + ex.Message;
                    return false;
                }
                finally
                {
                    if (writeToDB) db.CloseConnection();
                }
                return true;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[read1kCardP]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[read1kCardP] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }

        /// <summary>
        /// Deletes a program stored in the specified sector of the card
        /// </summary>
        /// <param name="sector">Sector in which the program to be deleted is stored</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        private bool delProgramP(short sector)
        {
            try
            {
                byte[] blank = new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
                for (int i = 0; i < 3; i++)
                {
                    if (!PrinterCardComm.RfidCardRequestAndWriteData(sector * 4 + i, blank, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[delProgramP]: Error writing block " + Convert.ToString((sector * 4 + i)) + System.Environment.NewLine + CardComm.lastErr);
                        return false;
                    }
                }
                //Clear Directory Record
                return updateDirectoryP(sector, 0, 0);
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[delProgramP]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[delProgramP] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }

        /// <summary>
        /// Updates directory information of the card when a program is added/deleted
        /// </summary>
        /// <param name="sector">The sector in which the program is stored</param>
        /// <param name="merchant">The merchant who owns the program</param>
        /// <param name="programType">The program type</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        private bool updateDirectoryP(short sector, short merchant, byte programType)
        {
            if (_cardType == MIFARE_1K) return update1kDirectoryP(sector, merchant, programType);
            else if (_cardType == MIFARE_4K) return update4kDirectoryP(sector, merchant, programType);
            else return false;
        }

        private bool update1kDirectoryP(short sector, short merchant, byte programType)
        {
            try
            {
                short blkNum = Convert.ToInt16(sector / 5);
                //if (sector % 5 != 0) blkNum++;
                blkNum = Convert.ToInt16(blkNum + 61);

                short byteNum = sector;
                if (byteNum >= 5) byteNum = Convert.ToInt16(byteNum % 5);
                //if (byteNum == 0) byteNum = Convert.ToInt16(sector/5);
                byteNum = Convert.ToInt16((byteNum) * 3);

                //Read original block data
                byte[] data = new byte[16];

                if (CardComm.currCardID == String.Empty)
                {
                    if (debug) Log.WriteLog("Error", "[update1kDirectoryP]: No Card Detected");
                    lastErr = "No Card Detected!";
                    return false;
                }

                if (CardComm.AuthBlock(63, get_diversify_key(master, CardComm.currCardIDArr, 15)) == false)
                {
                    if (debug) Log.WriteLog("Error", "[update1kDirectoryP]: Authentication Failed for Block 63");
                    lastErr = "Authentication Failed!";
                    return false;
                }

                if (!PrinterCardComm.RfidCardRequestAndReadData(blkNum, data, 0))
                {
                    if (debug) Log.WriteLog("Error", "[update1kDirectoryP]: Error Reading Block " + Convert.ToString(blkNum));
                    lastErr = "Error Reading Card";
                    return false;
                }

                byte[] shortArr = new byte[2];
                shortArr = BitConverter.GetBytes(merchant);
                data[byteNum] = shortArr[0];
                data[byteNum + 1] = shortArr[1];
                data[byteNum + 2] = programType;

                if (!PrinterCardComm.RfidCardRequestAndWriteData(blkNum, data, 0))
                {
                    lastErr = "Error Updating Directory Info!";
                    return false;
                }

                //Update sectors used 
                string sect = Convert.ToString(sectorsUsed, 2);
                sect = sect.PadLeft(64, '0');
                char[] charArr = sect.ToCharArray();
                Array.Reverse(charArr);
                sect = new string(charArr);
                sect = sect.Remove(sector, 1);
                if (merchant == 0)
                    sect = sect.Insert(sector, "0");
                else
                    sect = sect.Insert(sector, "1");

                charArr = sect.ToCharArray();
                Array.Reverse(charArr);
                long sectLng = Convert.ToInt64(new string(charArr), 2);
                if (!PrinterCardComm.RfidCardRequestAndReadData(60, data, 0))
                {
                    if (debug) Log.WriteLog("Error", "[update1kDirectoryP]: Error Writing Block " + Convert.ToString(blkNum));
                    lastErr = "Error Reading Card";
                    return false;
                }

                byte[] lngArr = new byte[8];
                lngArr = BitConverter.GetBytes(sectLng);
                for (int i = 0; i < 8; i++)
                {
                    data[i] = lngArr[i];
                }

                if (!PrinterCardComm.RfidCardRequestAndWriteData(60, data, 0))
                {
                    if (debug) Log.WriteLog("Error", "[update1kDirectoryP]: Error Reading Block 60");
                    lastErr = "Error Updating Directory Info!";
                    return false;
                }

                DirData currDir = new DirData();
                currDir.merchantID = merchant;
                currDir.programType = programType;
                if (currDir.merchantID != 0 && merchantDict.ContainsKey(currDir) == false) merchantDict.Add(currDir, Convert.ToInt16(sector));
                return true;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[update1kDirectoryP]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[update1kDirectoryP] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }

        private bool update4kDirectoryP(short sector, short merchant, byte programType)
        {
            try
            {
                short blkNum = Convert.ToInt16(sector / 5);
                if (sector % 5 != 0) blkNum++;
                blkNum = Convert.ToInt16(blkNum + 243);

                short byteNum = sector;
                if (byteNum > 5) byteNum = Convert.ToInt16(byteNum % 5);
                if (byteNum == 0) byteNum = Convert.ToInt16(sector / 5);
                byteNum = Convert.ToInt16((byteNum - 1) * 3);

                //Read original block data
                byte[] data = new byte[16];

                if (CardComm.currCardID == String.Empty)
                {
                    if (debug) Log.WriteLog("Error", "[update4kDirectoryP]: No Card Detected");
                    lastErr = "No Card Detected!";
                    return false;
                }

                //if (CardComm.AuthBlock(255, KEYA))
                if (!CardComm.AuthBlock(255, get_diversify_key(master, CardComm.currCardIDArr, 39)))
                {
                    if (debug) Log.WriteLog("Error", "[update4kDirectoryP]: Authentication Failed for Block 255");
                    lastErr = "Authentication Failed!";
                    return false;
                }

                if (!PrinterCardComm.RfidCardRequestAndReadData(blkNum, data, 0))
                {
                    if (debug) Log.WriteLog("Error", "[update4kDirectoryP]: Error Reading Block " + Convert.ToString(blkNum));
                    lastErr = "Error Reading Card";
                    return false;
                }

                byte[] shortArr = new byte[2];
                shortArr = BitConverter.GetBytes(merchant);
                data[byteNum] = shortArr[0];
                data[byteNum + 1] = shortArr[1];
                data[byteNum + 2] = programType;

                if (!PrinterCardComm.RfidCardRequestAndWriteData(blkNum, data, 0))
                {
                    if (debug) Log.WriteLog("Error", "[update4kDirectoryP]: Error Writing Block " + Convert.ToString(blkNum));
                    lastErr = "Error Updating Directory Info!";
                    return false;
                }

                //Update sectors used 
                string sect = Convert.ToString(sectorsUsed, 2);
                sect = sect.PadLeft(64, '0');
                char[] charArr = sect.ToCharArray();
                Array.Reverse(charArr);
                sect = new string(charArr);
                sect = sect.Remove(sector, 1);
                if (merchant == 0)
                    sect = sect.Insert(sector, "0");
                else
                    sect = sect.Insert(sector, "1");

                charArr = sect.ToCharArray();
                Array.Reverse(charArr);
                long sectLng = Convert.ToInt64(new string(charArr), 2);
                if (!PrinterCardComm.RfidCardRequestAndReadData(243, data, 0))
                {
                    if (debug) Log.WriteLog("Error", "[update4kDirectoryP]: Error Reading Block 243");
                    lastErr = "Error Reading Card";
                    return false;
                }

                byte[] lngArr = new byte[8];
                lngArr = BitConverter.GetBytes(sectLng);
                for (int i = 0; i < 8; i++)
                {
                    data[i] = lngArr[i];
                }

                if (!PrinterCardComm.RfidCardRequestAndWriteData(243, data, 0))
                {
                    if (debug) Log.WriteLog("Error", "[update4kDirectoryP]: Error Writing Block 243");
                    lastErr = "Error Updating Directory Info!";
                    return false;
                }

                DirData currDir = new DirData();
                currDir.merchantID = merchant;
                currDir.programType = programType;
                if (currDir.merchantID != 0 && merchantDict.ContainsKey(currDir) == false) merchantDict.Add(currDir, Convert.ToInt16(sector));
                return true;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[update4kDirectoryP]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[update4kDirectoryP] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }

        #region "EPurse"
        /// <summary>
        /// Adds a new ePurse program to the card
        /// </summary>
        /// <param name="data">ePurse structure containing the new purse data</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        private bool addEPurseP(ePurse data)
        {
            try
            {
                int nextSector = getProgramSector(data.merchantID, 4);
                if (nextSector == 0) nextSector = getNextSector();

                if (nextSector >= 0)
                {
                    byte[] blk = new byte[16];
                    byte[] shortArr = new byte[2];


                    //Program Type Purse: 4
                    blk[0] = 4;

                    //Merchant ID
                    shortArr = BitConverter.GetBytes(data.merchantID);
                    blk[1] = shortArr[0];
                    blk[2] = shortArr[1];

                    //Program Join Date
                    blk[3] = Convert.ToByte(data.programJoinDate.Year - 2000); //Year (2 digits)
                    blk[4] = Convert.ToByte(data.programJoinDate.Month); //Month
                    blk[5] = Convert.ToByte(data.programJoinDate.Day); //Day

                    //Purse Seq Number
                    shortArr = BitConverter.GetBytes(data.purseSeqNumber);
                    blk[6] = shortArr[0];
                    blk[7] = shortArr[1];

                    //Expiry Date
                    blk[8] = Convert.ToByte(data.purseExpiry.Year - 2000); //Year (2 digits)
                    blk[9] = Convert.ToByte(data.purseExpiry.Month); //Month
                    blk[10] = Convert.ToByte(data.purseExpiry.Day); //Day

                    short purseDataBlock = Convert.ToInt16(nextSector * 4);

                    //Authenticate
                    if (!CardComm.AuthBlock(purseDataBlock + 3, get_diversify_key(master, CardComm.currCardIDArr, nextSector)))
                    {
                        if (debug) Log.WriteLog("Error", "[addEPurseP]: Authentication Failed for Block " + Convert.ToString(purseDataBlock + 3));
                        lastErr = "Error Authenticating Card!";
                        return false;
                    }
                    //Write block 0 data
                    if (!PrinterCardComm.RfidCardRequestAndWriteData(purseDataBlock, blk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[addEPurseP]: Error writing to block" + Convert.ToString(purseDataBlock) + System.Environment.NewLine + CardComm.lastErr);
                        lastErr = "Error Writing to Card";
                        return false;
                    }

                    //Membership ID
                    blk = Encoding.ASCII.GetBytes(data.memberID.PadRight(16).Substring(0, 16));

                    //Write block 1 data
                    if (!PrinterCardComm.RfidCardRequestAndWriteData(purseDataBlock + 1, blk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[addEPurseP]: Error writing to block" + Convert.ToString(purseDataBlock + 1) + System.Environment.NewLine + CardComm.lastErr);
                        lastErr = "Error Writing to Card";
                        return false;
                    }

                    //Initialize Purse
                    byte[] purseArr = BitConverter.GetBytes(data.purseValue);
                    if (!PrinterCardComm.RfidCardRequestAndWriteData(purseDataBlock + 2, purseArr, 1))
                    {
                        if (debug) Log.WriteLog("Error", "[addEPurseP]: Error initializing purse" + System.Environment.NewLine + CardComm.lastErr);
                        lastErr = "Error Initializing Purse ";
                        return false;
                    }

                    //Update Directory
                    if (updateDirectoryP(Convert.ToInt16(nextSector), data.merchantID, 4))
                    {
                        //Add ePurse Program to database
                        try
                        {
                            if (debug)
                            {
                                Log.WriteLog("Database", "Executing SP: [dbo].[AddEPurseP] '" + cardID + "'," +
                                    Convert.ToString(data.merchantID) + ",'" + data.purseExpiry.ToString("yyyy-MM-dd") + "'");
                            }

                            if (writeToDB)
                            {
                                db.OpenConnection();

                                SqlCommand cmd = db.MakeCommand_SP("[dbo].[AddEPurseP]");
                                cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                                cmd.Parameters["@cardIDM"].Value = cardID;
                                cmd.Parameters.Add(new SqlParameter("@merchantID", SqlDbType.VarChar));
                                cmd.Parameters["@merchantID"].Value = data.merchantID;
                                cmd.Parameters.Add(new SqlParameter("@expiry", SqlDbType.VarChar));
                                cmd.Parameters["@expiry"].Value = data.purseExpiry;

                                cmd.ExecuteNonQuery();
                            }
                        }
                        catch (SqlException ex)
                        {
                            if (debug) Log.WriteLog("Exception", "[addEPurseP]:SQL Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                            lastErr = "[addEPurseP] : Error updating database - " + ex.Message;
                            return false;
                        }
                        finally
                        {
                            if (writeToDB) db.CloseConnection();
                        }
                        return true;
                    }
                    else return false;
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[addEPurseP]: Card is out of memeory");
                    lastErr = "Out of Memory!";
                    return false;
                }
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[addEPurseP]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[addEPurseP] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }

        /// <summary>
        /// Adds a new ePurse program to the card with specified order
        /// </summary>
        /// <param name="data">ePurse structure containing the new purse data</param>
        /// <param name="order">The order of the ePurse program added by the same merchant. i.e. The same merchant can add 2 ePurse programs with order 1 and 2</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        private bool addEPurseP(ePurse data, int order)
        {
            try
            {
                int nextSector = getProgramSector(data.merchantID, 4);
                if (nextSector == 0) nextSector = getNextSector();

                if (nextSector >= 0)
                {
                    byte[] blk = new byte[16];
                    byte[] shortArr = new byte[2];


                    //Program Type Purse: 4
                    blk[0] = BitConverter.GetBytes((4 << 4) & order)[0]; ;

                    //Merchant ID
                    shortArr = BitConverter.GetBytes(data.merchantID);
                    blk[1] = shortArr[0];
                    blk[2] = shortArr[1];

                    //Program Join Date
                    blk[3] = Convert.ToByte(data.programJoinDate.Year - 2000); //Year (2 digits)
                    blk[4] = Convert.ToByte(data.programJoinDate.Month); //Month
                    blk[5] = Convert.ToByte(data.programJoinDate.Day); //Day

                    //Purse Seq Number
                    shortArr = BitConverter.GetBytes(data.purseSeqNumber);
                    blk[6] = shortArr[0];
                    blk[7] = shortArr[1];

                    //Expiry Date
                    blk[8] = Convert.ToByte(data.purseExpiry.Year - 2000); //Year (2 digits)
                    blk[9] = Convert.ToByte(data.purseExpiry.Month); //Month
                    blk[10] = Convert.ToByte(data.purseExpiry.Day); //Day

                    short purseDataBlock = Convert.ToInt16(nextSector * 4);

                    //Authenticate
                    if (!CardComm.AuthBlock(purseDataBlock + 3, get_diversify_key(master, CardComm.currCardIDArr, nextSector)))
                    {
                        if (debug) Log.WriteLog("Error", "[addEPurseP]: Authentication Failed for Block " + Convert.ToString(purseDataBlock + 3));
                        lastErr = "Error Authenticating Card!";
                        return false;
                    }
                    //Write block 0 data
                    if (!CardComm.WriteBlock(purseDataBlock, blk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[addEPurseP]: Error writing to block" + Convert.ToString(purseDataBlock) + System.Environment.NewLine + CardComm.lastErr);
                        lastErr = "Error Writing to Card";
                        return false;
                    }

                    //Membership ID
                    blk = Encoding.ASCII.GetBytes(data.memberID.PadRight(16).Substring(0, 16));

                    //Write block 1 data
                    if (!CardComm.WriteBlock(purseDataBlock + 1, blk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[addEPurseP]: Error writing to block" + Convert.ToString(purseDataBlock + 1) + System.Environment.NewLine + CardComm.lastErr);
                        lastErr = "Error Writing to Card";
                        return false;
                    }

                    //Initialize Purse
                    byte[] purseArr = BitConverter.GetBytes(data.purseValue);
                    if (!CardComm.WriteBlock(purseDataBlock + 2, purseArr, 1))
                    {
                        if (debug) Log.WriteLog("Error", "[addEPurseP]: Error initializing purse" + System.Environment.NewLine + CardComm.lastErr);
                        lastErr = "Error Initializing Purse ";
                        return false;
                    }

                    //Update Directory
                    if (updateDirectoryP(Convert.ToInt16(nextSector), data.merchantID, 4))
                    {
                        //Add ePurse Program to database
                        try
                        {
                            if (debug)
                            {
                                Log.WriteLog("Database", "Executing SP: [dbo].[AddEPurseP] '" + cardID + "'," +
                                    Convert.ToString(data.merchantID) + ",'" + data.purseExpiry.ToString("yyyy-MM-dd") + "'");
                            }

                            if (writeToDB)
                            {
                                db.OpenConnection();

                                SqlCommand cmd = db.MakeCommand_SP("[dbo].[AddEPurseP]");
                                cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                                cmd.Parameters["@cardIDM"].Value = cardID;
                                cmd.Parameters.Add(new SqlParameter("@merchantID", SqlDbType.VarChar));
                                cmd.Parameters["@merchantID"].Value = data.merchantID;
                                cmd.Parameters.Add(new SqlParameter("@expiry", SqlDbType.VarChar));
                                cmd.Parameters["@expiry"].Value = data.purseExpiry;

                                cmd.ExecuteNonQuery();
                            }
                        }
                        catch (SqlException ex)
                        {
                            if (debug) Log.WriteLog("Exception", "[addEPurseP]:SQL Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                            lastErr = "[addEPurseP] : Error updating database - " + ex.Message;
                            return false;
                        }
                        finally
                        {
                            if (writeToDB) db.CloseConnection();
                        }
                        return true;
                    }
                    else return false;
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[addEPurseP]: Card is out of memeory");
                    lastErr = "Out of Memory!";
                    return false;
                }
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[addEPurseP]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[addEPurseP] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }

        /// <summary>
        /// Reads ePurse data
        /// </summary>
        /// <param name="sect">Sector to read</param>
        /// <param name="pData">Holds the purse data to be read from the card</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        private bool readEPurseP(short sect, ref ePurse pData)
        {
            byte[] data = new byte[16];

            try
            {
                if (CardComm.currCardID == String.Empty)
                {
                    if (debug) Log.WriteLog("Error", "[readEPurseP]: No Card Detected");
                    lastErr = "No Card Detected!";
                    return false;
                }

                if (!CardComm.AuthBlock((sect * 4) + 3, get_diversify_key(master, CardComm.currCardIDArr, sect)))
                {
                    if (debug) Log.WriteLog("Error", "[readEPurseP]: Authentication Failed for Block " + Convert.ToString((sect * 4) + 3));
                    lastErr = "Error Authenticating Card!";
                    return false;
                }
                if (PrinterCardComm.RfidCardRequestAndReadData(sect * 4, data, 0))
                {
                    if (data[0] != 4)
                    {
                        if (debug) Log.WriteLog("Error", "[readEPurseP]: Sector is not being used for ePurse program. Data read: " + Convert.ToString(data[0]));
                        lastErr = "Sector is not being used for ePurse program!";
                        return false;
                    }
                    pData.merchantID = BitConverter.ToInt16(data, 1);
                    pData.programJoinDate = new DateTime(data[3] + 2000, data[4], data[5]);
                    pData.purseSeqNumber = BitConverter.ToInt16(data, 6);
                    pData.purseExpiry = new DateTime(data[8] + 2000, data[9], data[10]);
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[readEPurseP]: Error reading block " + Convert.ToString(sect * 4) + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                if (PrinterCardComm.RfidCardRequestAndReadData((sect * 4) + 1, data, 0))
                {
                    pData.memberID = Encoding.ASCII.GetString(data, 0, 16);
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[readEPurseP]: Error reading block " + Convert.ToString(sect * 4 + 1) + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                byte[] purseArr = new byte[4];
                if (!PrinterCardComm.RfidCardRequestAndReadData(sect * 4 + 2, purseArr, 1))
                {
                    if (debug) Log.WriteLog("Error", "[readEPurseP]: Error reading block " + Convert.ToString(sect * 4 + 2) + System.Environment.NewLine + CardComm.lastErr);
                    lastErr = "Error reading purse!";
                    return false;
                }
                else
                {
                    Array.Reverse(purseArr, 0, 4);
                    pData.purseValue = BitConverter.ToInt32(purseArr, 0);
                }


                return true;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[readEPurseP]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[readEPurseP] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }

        /// <summary>
        /// Edits purse data (Top up/deduct value, change expiry date)
        /// </summary>
        /// <param name="sect">Sector containing the data to be edited</param>
        /// <param name="amt">Amount to top up/deduct. Deduction amount is negative.</param>
        /// <param name="expiry">Expiry date</param>
        /// <param name="merchantID">ID of the merchant where the transaction was performed</param>
        /// <param name="memberID">Member ID of the card holder</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        private bool editEPurseP(short sect, int amt, DateTime expiry, int merchantID, string memberID)
        {
            ePurse pdata = new ePurse();
            try
            {
                if (!readEPurseP(sect, ref pdata)) return false;

                if (amt != 0)
                    pdata.purseSeqNumber = Convert.ToInt16(pdata.purseSeqNumber + 1);
                if (pdata.purseValue + amt < 0)
                {
                    if (debug) Log.WriteLog("Error", "[editEPurseP]: New purse balance cannot be negative!");
                    lastErr = "New purse balance cannot be negative!";
                    return false;
                }
                pdata.purseExpiry = expiry;

                byte[] blk = new byte[16];
                byte[] shortArr = new byte[2];

                //Program Type Purse: 4
                blk[0] = 4;

                //Merchant ID
                shortArr = BitConverter.GetBytes(pdata.merchantID);
                blk[1] = shortArr[0];
                blk[2] = shortArr[1];

                //Program Join Date
                blk[3] = Convert.ToByte(pdata.programJoinDate.Year - 2000); //Year (2 digits)
                blk[4] = Convert.ToByte(pdata.programJoinDate.Month); //Month
                blk[5] = Convert.ToByte(pdata.programJoinDate.Day); //Day

                //Purse Seq Number
                shortArr = BitConverter.GetBytes(pdata.purseSeqNumber);
                blk[6] = shortArr[0];
                blk[7] = shortArr[1];

                //Expiry Date
                blk[8] = Convert.ToByte(pdata.purseExpiry.Year - 2000); //Year (2 digits)
                blk[9] = Convert.ToByte(pdata.purseExpiry.Month); //Month
                blk[10] = Convert.ToByte(pdata.purseExpiry.Day); //Day

                //Authenticate
                if (!CardComm.AuthBlock((sect * 4) + 3, get_diversify_key(master, CardComm.currCardIDArr, sect)))
                {
                    if (debug) Log.WriteLog("Error", "[editEPurseP]: Authentication Failed for Block " + Convert.ToString((sect * 4) + 3));
                    lastErr = "Error Authenticating Card!";
                    return false;
                }

                //Write block 0 data
                if (!PrinterCardComm.RfidCardRequestAndWriteData(sect * 4, blk, 0))
                {
                    if (debug) Log.WriteLog("Error", "[editEPurseP]: Error writing block " + Convert.ToString(sect * 4) + System.Environment.NewLine + CardComm.lastErr);
                    lastErr = "Error writing to card";
                    return false;
                }

                //Membership ID
                blk = Encoding.ASCII.GetBytes(memberID.PadRight(16).Substring(0, 16));

                //Write block 1 data
                if (!PrinterCardComm.RfidCardRequestAndWriteData(sect * 4 + 1, blk, 0))
                {
                    if (debug) Log.WriteLog("Error", "[editEPurseP]: Error writing block " + Convert.ToString(sect * 4 + 1) + System.Environment.NewLine + CardComm.lastErr);
                    lastErr = "Error writing to card";
                    return false;
                }

                if (amt != 0)
                {
                    //Increase/Decrease
                    byte[] purseArr = new byte[4];
                    if (amt > 0)
                    {
                        purseArr = BitConverter.GetBytes(amt);
                        if (!PrinterCardComm.RfidCardRequestAndWriteData(sect * 4 + 2, purseArr, 2))
                        {
                            if (debug) Log.WriteLog("Error", "[editEPurseP]: Error increasing purse value (block " + Convert.ToString(sect * 4 + 2) + ")\n" + CardComm.lastErr);
                            lastErr = "Error increasing purse value";
                            return false;
                        }
                    }
                    else
                    {
                        amt = amt * -1;
                        purseArr = BitConverter.GetBytes(amt);
                        if (!PrinterCardComm.RfidCardRequestAndWriteData(sect * 4 + 2, purseArr, 3))
                        {
                            if (debug) Log.WriteLog("Error", "[editEPurseP]: Error deducting purse value (block " + Convert.ToString(sect * 4 + 2) + ")\n" + CardComm.lastErr);
                            lastErr = "Error deducting purse value";
                            return false;
                        }
                    }
                }

                if (writeToDB)
                {

                    try
                    {
                        db.OpenConnection();
                        SqlCommand cmd = db.MakeCommand_SP("[dbo].[TopupDeductEPurse]");
                        cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                        cmd.Parameters["@cardIDM"].Value = cardID;
                        cmd.Parameters.Add(new SqlParameter("@merchantID", SqlDbType.Int));
                        cmd.Parameters["@merchantID"].Value = merchantID;
                        cmd.Parameters.Add(new SqlParameter("@txnvalue", SqlDbType.Int));
                        cmd.Parameters["@txnvalue"].Value = amt;
                        cmd.Parameters.Add(new SqlParameter("@newbalance", SqlDbType.Int));
                        cmd.Parameters["@newbalance"].Value = pdata.purseValue + amt;
                        cmd.Parameters.Add(new SqlParameter("@purseseq", SqlDbType.Int));
                        cmd.Parameters["@purseseq"].Value = pdata.purseSeqNumber;

                        cmd.ExecuteNonQuery();
                    }

                    catch (SqlException ex)
                    {

                        if (debug) Log.WriteLog("Exception", "[editEPurseP]:SQL Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                        lastErr = "[editEPurseP] : Error updating database - " + ex.Message;
                        return false;
                    }
                    finally
                    {
                        if (writeToDB) db.CloseConnection();
                    }
                }
                return true;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[editEPurseP]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[editEPurseP] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }
        #endregion

        #region "Loyalty"
        /// <summary>
        /// Adds a new loyalty program to the card
        /// </summary>
        /// <param name="data">LoyaltyData structure containing the new loyalty data </param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        private bool addLoyaltyP(LoyaltyData data)
        {
            try
            {
                int nextSector = getProgramSector(data.merchantID, 2, 1);
                if (nextSector == 0) nextSector = getNextSector();


                if (nextSector >= 0)
                {

                    byte[] blk = new byte[16];
                    byte[] shortArr = new byte[2];
                    byte[] intArr = new byte[4];

                    //Program Type Loyalty: 2
                    blk[0] = BitConverter.GetBytes((2 << 4) | 1)[0];

                    //Merchant ID
                    shortArr = BitConverter.GetBytes(data.merchantID);
                    blk[1] = shortArr[0];
                    blk[2] = shortArr[1];

                    //Program Join Date
                    blk[3] = Convert.ToByte(data.programJoinDate.Year - 2000); //Year (2 digits)
                    blk[4] = Convert.ToByte(data.programJoinDate.Month); //Month
                    blk[5] = Convert.ToByte(data.programJoinDate.Day); //Day

                    //Points Seq Number
                    shortArr = BitConverter.GetBytes(data.pointsSeq);
                    blk[6] = shortArr[0];
                    blk[7] = shortArr[1];

                    //Points
                    intArr = BitConverter.GetBytes(data.points);
                    blk[8] = intArr[0];
                    blk[9] = intArr[1];
                    blk[10] = intArr[2];
                    blk[11] = intArr[3];

                    //Expiry Date
                    blk[12] = Convert.ToByte(data.expiryDate.Year - 2000); //Year (2 digits)
                    blk[13] = Convert.ToByte(data.expiryDate.Month); //Month
                    blk[14] = Convert.ToByte(data.expiryDate.Day); //Day

                    short loyaltyDataBlock = Convert.ToInt16(nextSector * 4);

                    //Authenticate
                    if (!CardComm.AuthBlock(loyaltyDataBlock + 3, get_diversify_key(master, CardComm.currCardIDArr, nextSector)))
                    {
                        if (debug) Log.WriteLog("Error", "[addLoyaltyP]: Authentication Failed for Block " + Convert.ToString(loyaltyDataBlock + 3));
                        lastErr = "Error Authenticating Card!";
                        return false;
                    }
                    //Write block 0 data
                    if (!PrinterCardComm.RfidCardRequestAndWriteData(loyaltyDataBlock, blk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[addLoyaltyP]: Error writing block " + Convert.ToString(loyaltyDataBlock) + System.Environment.NewLine + CardComm.lastErr);
                        lastErr = "Error Writing to Card";
                        return false;
                    }

                    //Membership ID
                    blk = Encoding.ASCII.GetBytes(data.memberID.PadRight(16).Substring(0, 16));

                    //Write block 1 data
                    if (!PrinterCardComm.RfidCardRequestAndWriteData(loyaltyDataBlock + 1, blk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[addLoyaltyP]: Error writing block " + Convert.ToString(loyaltyDataBlock + 1) + System.Environment.NewLine + CardComm.lastErr);
                        lastErr = "Error Writing to Card";
                        return false;
                    }

                    //Update Directory
                    if (updateDirectoryP(Convert.ToInt16(nextSector), data.merchantID, BitConverter.GetBytes((2 << 4) | 1)[0]))
                    {
                        if (debug)
                        {
                            Log.WriteLog("Database", "Executing SP: [dbo].[addLoyaltyP] '" + cardID + "'," +
                                Convert.ToString(data.merchantID) + ",'" + data.expiryDate.ToString("yyyy-MM-dd") + "'");
                        }
                        if (writeToDB)
                        {
                            //Add loyalty program to database
                            try
                            {
                                db.OpenConnection();
                                SqlCommand cmd = db.MakeCommand_SP("[dbo].[addLoyaltyP]");
                                cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                                cmd.Parameters["@cardIDM"].Value = cardID;
                                cmd.Parameters.Add(new SqlParameter("@merchantID", SqlDbType.VarChar));
                                cmd.Parameters["@merchantID"].Value = data.merchantID;
                                cmd.Parameters.Add(new SqlParameter("@expiry", SqlDbType.VarChar));
                                cmd.Parameters["@expiry"].Value = data.expiryDate;
                                cmd.ExecuteNonQuery();

                            }
                            catch (SqlException ex)
                            {
                                if (debug) Log.WriteLog("Exception", "[addEPurseP]:SQL Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);

                                lastErr = "[addEPurseP] : Error updating database - " + ex.Message;
                                return false;
                            }
                            finally
                            {
                                if (writeToDB) db.CloseConnection();
                            }
                        }
                        return true;
                    }
                    else
                    {
                        if (debug) Log.WriteLog("Error", "[addEPurseP]: Error updating directory" + System.Environment.NewLine + CardComm.lastErr);
                        return false;
                    }

                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[addLoyaltyP]: Card has insufficient memory");
                    lastErr = "Out of Memory!";
                    return false;
                }
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[addLoyaltyP]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[addLoyaltyP] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }

        /// <summary>
        /// Adds a new loyalty program to the card with the specified order
        /// </summary>
        /// <param name="data">LoyaltyData structure containing the new loyalty data </param>
        /// <param name="order">The order of the loyalty program added by the same merchant. i.e. The same merchant can add 2 loyalty programs with order 1 and 2</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        private bool addLoyaltyP(LoyaltyData data, short order)
        {
            try
            {
                int nextSector = getProgramSector(data.merchantID, 2, order);
                if (nextSector == 0) nextSector = getNextSector();


                if (nextSector >= 0)
                {

                    byte[] blk = new byte[16];
                    byte[] shortArr = new byte[2];
                    byte[] intArr = new byte[4];

                    //Program Type Loyalty: 2
                    blk[0] = BitConverter.GetBytes((2 << 4) | order)[0];

                    //Merchant ID
                    shortArr = BitConverter.GetBytes(data.merchantID);
                    blk[1] = shortArr[0];
                    blk[2] = shortArr[1];

                    //Program Join Date
                    blk[3] = Convert.ToByte(data.programJoinDate.Year - 2000); //Year (2 digits)
                    blk[4] = Convert.ToByte(data.programJoinDate.Month); //Month
                    blk[5] = Convert.ToByte(data.programJoinDate.Day); //Day

                    //Points Seq Number
                    shortArr = BitConverter.GetBytes(data.pointsSeq);
                    blk[6] = shortArr[0];
                    blk[7] = shortArr[1];

                    //Points
                    intArr = BitConverter.GetBytes(data.points);
                    blk[8] = intArr[0];
                    blk[9] = intArr[1];
                    blk[10] = intArr[2];
                    blk[11] = intArr[3];

                    //Expiry Date
                    blk[12] = Convert.ToByte(data.expiryDate.Year - 2000); //Year (2 digits)
                    blk[13] = Convert.ToByte(data.expiryDate.Month); //Month
                    blk[14] = Convert.ToByte(data.expiryDate.Day); //Day

                    short loyaltyDataBlock = Convert.ToInt16(nextSector * 4);

                    //Authenticate
                    if (!CardComm.AuthBlock(loyaltyDataBlock + 3, get_diversify_key(master, CardComm.currCardIDArr, nextSector)))
                    {
                        if (debug) Log.WriteLog("Error", "[addLoyaltyP]: Authentication Failed for Block " + Convert.ToString(loyaltyDataBlock + 3));
                        lastErr = "Error Authenticating Card!";
                        return false;
                    }
                    //Write block 0 data
                    if (!PrinterCardComm.RfidCardRequestAndWriteData(loyaltyDataBlock, blk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[addLoyaltyP]: Error writing block " + Convert.ToString(loyaltyDataBlock) + System.Environment.NewLine + CardComm.lastErr);
                        lastErr = "Error Writing to Card";
                        return false;
                    }

                    //Membership ID
                    blk = Encoding.ASCII.GetBytes(data.memberID.PadRight(16).Substring(0, 16));

                    //Write block 1 data
                    if (!PrinterCardComm.RfidCardRequestAndWriteData(loyaltyDataBlock + 1, blk, 0))
                    {
                        if (debug) Log.WriteLog("Error", "[addLoyaltyP]: Error writing block " + Convert.ToString(loyaltyDataBlock + 1) + System.Environment.NewLine + CardComm.lastErr);
                        lastErr = "Error Writing to Card";
                        return false;
                    }

                    //Update Directory
                    if (updateDirectoryP(Convert.ToInt16(nextSector), data.merchantID, BitConverter.GetBytes((2 << 4) | order)[0]))
                    {
                        if (debug)
                        {
                            Log.WriteLog("Database", "Executing SP: [dbo].[addLoyaltyP] '" + cardID + "'," +
                                Convert.ToString(data.merchantID) + ",'" + data.expiryDate.ToString("yyyy-MM-dd") + "'");
                        }
                        if (writeToDB)
                        {
                            //Add loyalty program to database
                            try
                            {
                                db.OpenConnection();
                                SqlCommand cmd = db.MakeCommand_SP("[dbo].[addLoyaltyP]");
                                cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                                cmd.Parameters["@cardIDM"].Value = cardID;
                                cmd.Parameters.Add(new SqlParameter("@merchantID", SqlDbType.VarChar));
                                cmd.Parameters["@merchantID"].Value = data.merchantID;
                                cmd.Parameters.Add(new SqlParameter("@expiry", SqlDbType.VarChar));
                                cmd.Parameters["@expiry"].Value = data.expiryDate;
                                cmd.ExecuteNonQuery();

                            }
                            catch (SqlException ex)
                            {
                                if (debug) Log.WriteLog("Exception", "[addEPurseP]:SQL Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);

                                lastErr = "[addEPurseP] : Error updating database - " + ex.Message;
                                return false;
                            }
                            finally
                            {
                                if (writeToDB) db.CloseConnection();
                            }
                        }
                        return true;
                    }
                    else
                    {
                        if (debug) Log.WriteLog("Error", "[addEPurseP]: Error updating directory" + System.Environment.NewLine + CardComm.lastErr);
                        return false;
                    }

                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[addLoyaltyP]: Card has insufficient memory");
                    lastErr = "Out of Memory!";
                    return false;
                }
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[addLoyaltyP]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[addLoyaltyP] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }

        /// <summary>
        /// Reads loyalty data from the card
        /// </summary>
        /// <param name="sect">Sector containing the loyalty data to be read</param>
        /// <param name="ldata">Holds the loyalty data to be read from the card</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        private bool readLoyaltyP(short sect, ref LoyaltyData ldata)
        {
            byte[] data = new byte[16];

            try
            {
                if (CardComm.currCardID == String.Empty)
                {
                    if (debug) Log.WriteLog("Error", "[readLoyaltyP]: No Card Detected");
                    lastErr = "No Card Detected!";
                    return false;
                }

                if (!CardComm.AuthBlock((sect * 4) + 3, get_diversify_key(master, CardComm.currCardIDArr, sect)))
                {
                    if (debug) Log.WriteLog("Error", "[readLoyaltyP]: Authentication Failed for Block " + Convert.ToString((sect * 4) + 3));
                    lastErr = "Error Authenticating Card!";
                    return false;
                }
                if (PrinterCardComm.RfidCardRequestAndReadData(sect * 4, data, 0))
                {
                    if (data[0] >> 4 != 2)
                    {
                        if (debug) Log.WriteLog("Error", "[readLoyaltyP]: Sector is not being used for Loyalty program. Data read: " + Convert.ToString(data[0]));
                        lastErr = "Sector is not being used for Loyalty program!";
                        return false;
                    }

                    ldata.merchantID = BitConverter.ToInt16(data, 1);
                    ldata.order = Convert.ToInt16(data[0] & 15);
                    ldata.programJoinDate = new DateTime(data[3] + 2000, data[4], data[5]);
                    ldata.pointsSeq = BitConverter.ToInt16(data, 6);
                    ldata.points = BitConverter.ToInt32(data, 8);
                    ldata.expiryDate = new DateTime(data[12] + 2000, data[13], data[14]);
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[readLoyaltyP]: Error reading block " + Convert.ToString(sect * 4) + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

                if (PrinterCardComm.RfidCardRequestAndReadData((sect * 4) + 1, data, 0))
                {
                    ldata.memberID = Encoding.ASCII.GetString(data, 0, 16);
                    return true;
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[readLoyaltyP]: Error reading block " + Convert.ToString(sect * 4 + 1) + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }

            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[readLoyaltyP]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[readLoyaltyP] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }

        /// <summary>
        /// Edit the loyalty data in the card (Add/Deduct points, change expiry date)
        /// </summary>
        /// <param name="sect">Sector in which the program is stored</param>
        /// <param name="order">Order of the loyalty program for this merchant</param>
        /// <param name="points">Points to be added/deducted. For deduction, points should be negative</param>
        /// <param name="expiry">Expiry date</param>
        /// <param name="merchantID">ID of the merchant where the transaction was performed</param>
        /// <param name="memberID">Member ID of the cardholder</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        private bool editLoyaltyP(short sect, short order, int points, DateTime expiry, int merchantID, string memberID)
        {
            LoyaltyData ldata = new LoyaltyData();
            try
            {
                if (readLoyaltyP(sect, ref ldata) == false) return false;

                if (points != 0)
                    ldata.pointsSeq = Convert.ToInt16(ldata.pointsSeq + 1);
                ldata.points = ldata.points + points;
                if (ldata.points < 0)
                {
                    if (debug) Log.WriteLog("Error", "[editLoyaltyP]: New points balance cannot be negative");
                    lastErr = "New points balance cannot be negative!";
                    return false;
                }
                ldata.expiryDate = expiry;

                byte[] blk = new byte[16];
                byte[] shortArr = new byte[2];
                byte[] intArr = new byte[4];

                //Program Type Loyalty: 2
                blk[0] = BitConverter.GetBytes((2 << 4) | order)[0];

                //Merchant ID
                shortArr = BitConverter.GetBytes(ldata.merchantID);
                blk[1] = shortArr[0];
                blk[2] = shortArr[1];

                //Program Join Date
                blk[3] = Convert.ToByte(ldata.programJoinDate.Year - 2000); //Year (2 digits)
                blk[4] = Convert.ToByte(ldata.programJoinDate.Month); //Month
                blk[5] = Convert.ToByte(ldata.programJoinDate.Day); //Day

                //Points Seq Number
                shortArr = BitConverter.GetBytes(ldata.pointsSeq);
                blk[6] = shortArr[0];
                blk[7] = shortArr[1];

                //Points
                intArr = BitConverter.GetBytes(ldata.points);
                blk[8] = intArr[0];
                blk[9] = intArr[1];
                blk[10] = intArr[2];
                blk[11] = intArr[3];

                //Expiry Date
                blk[12] = Convert.ToByte(ldata.expiryDate.Year - 2000); //Year (2 digits)
                blk[13] = Convert.ToByte(ldata.expiryDate.Month); //Month
                blk[14] = Convert.ToByte(ldata.expiryDate.Day); //Day

                //Authenticate
                if (!CardComm.AuthBlock((sect * 4) + 3, get_diversify_key(master, CardComm.currCardIDArr, sect)))
                {
                    if (debug) Log.WriteLog("Error", "[editLoyaltyP]: Authentication Failed for Block " + Convert.ToString((sect * 4) + 3));
                    lastErr = "Error Authenticating Card!";
                    return false;
                }

                //Write block 0 data
                if (!PrinterCardComm.RfidCardRequestAndWriteData(sect * 4, blk, 0))
                {
                    if (debug) Log.WriteLog("Error", "[editLoyaltyP]: Error writing block " + Convert.ToString(sect * 4) + System.Environment.NewLine + CardComm.lastErr);
                    lastErr = "Error writing to card";
                    return false;
                }

                //Member ID
                blk = Encoding.ASCII.GetBytes(memberID.PadRight(16).Substring(0, 16));
                //Write block 1 data
                if (!PrinterCardComm.RfidCardRequestAndWriteData(sect * 4 + 1, blk, 0))
                {
                    if (debug) Log.WriteLog("Error", "[editLoyaltyP]: Error writing block " + Convert.ToString(sect * 4 + 1) + System.Environment.NewLine + CardComm.lastErr);
                    lastErr = "Error writing to card";
                    return false;
                }

                //Add loyalty txn to database
                if (points != 0)
                {
                    if (debug)
                    {
                        Log.WriteLog("Database", "Executing SP: [dbo].[TopupDeductLoyalty] '" + cardID + "'" +
                            "," + Convert.ToString(merchantID) + "," + Convert.ToString(points) + "," + Convert.ToString(ldata.points) +
                            "," + Convert.ToString(ldata.pointsSeq));
                    }
                    if (writeToDB)
                    {
                        try
                        {

                            db.OpenConnection();
                            SqlCommand cmd = db.MakeCommand_SP("[dbo].[TopupDeductLoyalty]");
                            cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                            cmd.Parameters["@cardIDM"].Value = cardID;
                            cmd.Parameters.Add(new SqlParameter("@merchantID", SqlDbType.Int));
                            cmd.Parameters["@merchantID"].Value = merchantID;
                            cmd.Parameters.Add(new SqlParameter("@txnvalue", SqlDbType.Int));
                            cmd.Parameters["@txnvalue"].Value = points;
                            cmd.Parameters.Add(new SqlParameter("@newbalance", SqlDbType.Int));
                            cmd.Parameters["@newbalance"].Value = ldata.points;
                            cmd.Parameters.Add(new SqlParameter("@pointsseq", SqlDbType.Int));
                            cmd.Parameters["@pointsseq"].Value = ldata.pointsSeq;

                            cmd.ExecuteNonQuery();
                        }
                        catch (SqlException ex)
                        {

                            if (debug) Log.WriteLog("Exception", "[editLoyaltyP]:SQL Exception", ex.Message + System.Environment.NewLine + ex.InnerException);
                            lastErr = "[editEPurseP] : Error updating database - " + ex.Message;
                            return false;
                        }
                        finally
                        {
                            if (writeToDB) db.CloseConnection();
                        }
                    }
                }
                return true;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[editLoyaltyP]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[editLoyaltyP] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }
        #endregion

        /// <summary>
        /// Issues a new card and activates the loyalty points and ePurse functions
        /// </summary>
        /// <param name="pname">Name of the card holder</param>
        /// <param name="pDOB">Date of Birth</param>
        /// <param name="pgender">Gender - M: Male, F: Female</param>
        /// <param name="pmerchantID">Unique ID of the merchant issuing the card</param>
        /// <param name="pcardNum">Unique card number identifying the card</param>
        /// <param name="pterminalID">Terminal ID of the terminal which the card is issued from</param>
        /// <param name="pdata">Initial purse data of the card</param>
        /// <param name="ldata">Initial loyalty data of the card</param>
        /// <param name="pID">IC/Passport number of the card holder</param>
        /// <param name="pphone">Phone number of the card holder</param>
        /// <param name="pemail">Email address of the card holder</param>
        /// <param name="status">Card Status</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        public bool issueCardP(string pname, DateTime pDOB, char pgender, short pmerchantID, int pcardNum, short pterminalID, ePurse pdata, LoyaltyData ldata, string pID, string pphone, string pemail, byte status)
        {
            try
            {
                if (debug) Log.WriteLog("Invoke Method", "issueCardP(" + pname + "," + pDOB.ToString("yyyy-MM-dd") +
                    "," + pgender + "," + Convert.ToString(pmerchantID) + "," + Convert.ToString(pcardNum) + "," + Convert.ToString(pterminalID) +
                    ",[ePurse data - Merchant ID:" + Convert.ToString(pdata.merchantID) + ", Join Date:" + pdata.programJoinDate.ToString("yyyy-MM-dd") +
                    ", Expiry Date: " + pdata.purseExpiry.ToString("yyyy-MM-dd") + ", Purse Value:" + Convert.ToString(pdata.purseValue) + "], [Loyalty data - Merchant ID:" +
                    Convert.ToString(ldata.merchantID) + ", Join Date:" + ldata.programJoinDate + ", Expiry Date: " + ldata.expiryDate +
                    ", Points: " + ldata.points + "," + Convert.ToString(pID) + "," + pphone + "," + pemail + "," + status.ToString() + ")");

                if (_cardType == MIFARE_1K)
                {
                    if (!initialize1KCardP(pname, pDOB, pgender, pmerchantID, pcardNum, pterminalID, pID, pphone, pemail, status))
                        return false;
                }
                else if (_cardType == MIFARE_4K)
                {
                    if (!initialize4KCardP(pname, pDOB, pgender, pmerchantID, pcardNum, pterminalID, pID, pphone, pemail, status))
                        return false;
                }
                merchantDict.Clear();
                sectorsUsed = 0;
                if (addEPurseP(pdata))
                {
                    if (readCardP())
                    {
                        for (int i = 0; i < 5; i++)
                        {
                            ldata.order = Convert.ToInt16(i + 1);
                            if (!addLoyaltyP(ldata, ldata.order)) return false;
                            else
                            {
                                if (!readCardP()) return false;
                            }
                        }
                        return true;
                    }
                    else return false;
                }
                else return false;

            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[issueCardP]:Exception" + ex.Message, ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[issueCardP] " + ex.Message + (ex.InnerException == null ? "" : ":" + ex.InnerException.Message);
                return false;
            }
        }

        /// <summary>
        /// Reads the floor and room IDs where the card holder is staying
        /// </summary>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        public bool readRoomCardP()
        {
            //Read sector 1 blk 0
            try
            {
                byte[] data = new byte[16];
                byte[] shortArr = new byte[2];
                if (CardComm.AuthBlock(7, get_diversify_key(master, CardComm.currCardIDArr, 1)))
                {
                    if (PrinterCardComm.RfidCardRequestAndReadData(5, data, 0))
                    {
                        _roomID = data[0];
                        shortArr[0] = data[2];
                        shortArr[1] = data[1];
                        _bedID = BitConverter.ToInt16(shortArr, 0);
                        if (roomList.ContainsKey(_roomID))
                            _roomName = roomList[_roomID];
                        else
                            _roomName = "Unable to retrieve room name from settings file";
                        return true;
                    }
                    else
                    {
                        if (debug) Log.WriteLog("Error", "[readRoomCardP]: Error reading block 4" + System.Environment.NewLine + CardComm.lastErr);
                        return false;
                    }
                }
                else
                {
                    if (debug) Log.WriteLog("Error", "[readRoomCardP]: Error authenticating block 7" + System.Environment.NewLine + CardComm.lastErr);
                    return false;
                }
            }
            catch (Exception ex)
            {

                if (debug) Log.WriteLog("Exception", "[readRoomCardP]:Exception", ex.Message + (ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message));
                lastErr = "[readRoomCardP] " + ex.Message + (ex.InnerException == null ? "" : ":" + ex.InnerException.Message);
                return false;
            }
        }

        /// <summary>
        /// Reads the purse value of the card
        /// </summary>
        /// <param name="merchantID">Unique ID of the merchant issuing the card</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        public bool readPurseCardP(short merchantID)
        {
            try
            {
                if (readCardP())
                {
                    short sector = getProgramSector(merchantID, 4);
                    if (readEPurseP(Convert.ToInt16(sector), ref _purse))
                        return true;
                }
                return false;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[readRoomCardP]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[readRoomCardP] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }

        /// <summary>
        /// Reads loyalty data from the card
        /// </summary>
        /// <param name="merchantID">Unique ID of the merchant issuing the card</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        public bool readLoyaltyCardP(short merchantID)
        {
            try
            {
                LoyaltyData data = new LoyaltyData();
                int order = 0;
                short sector = 0;
                if (readCardP())
                {
                    _loyalty.Clear();
                    foreach (KeyValuePair<DirData, short> item in merchantDict)
                    {
                        if (item.Key.programType >> 4 == 2)
                        {
                            order = (item.Key.programType & 15);
                            sector = getProgramSector(merchantID, 2, Convert.ToInt16(order));
                            if (readLoyaltyP(Convert.ToInt16(sector), ref data))
                                _loyalty.Add(data);
                            else
                            {
                                _loyalty.Clear();
                                return false;
                            }
                        }
                    }
                    return true;

                }
                else return false;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[readLoyaltyCardP]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[readLoyaltyCardP] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }

        }

        /// <summary>
        /// Top up or deduct money from the card
        /// </summary>
        /// <param name="merchantID">Unique ID of the merchant issuing the card</param>
        /// <param name="amt">Amount in cents. E.g. $5.00 = 500, $2.50 = 250 </param>
        /// <param name="topup">True if it is a top up operation, False if it is a deduction operation</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        public bool topupDeductPurseP(short merchantID, int amt, bool topup)
        {
            try
            {
                if (debug) Log.WriteLog("Invoke Method", "topupDeductPurseP(" + Convert.ToString(merchantID) + "," +
                     Convert.ToString(amt) + "," + Convert.ToString(topup) + ")");

                if (readPurseCardP(merchantID))
                {
                    short sector = getProgramSector(merchantID, 4);
                    if (!topup) amt = amt * -1;
                    if (editEPurseP(Convert.ToInt16(sector), amt, this._purse.purseExpiry, merchantID, issueCardID.ToString()))
                        return true;
                }
                return false;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[topupDeductPurseP]:Exception", ex.Message + (ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message));
                lastErr = "[topupDeductPurseP] " + ex.Message + (ex.InnerException == null ? "" : ":" + ex.InnerException.Message);
                return false;
            }
        }

        /// <summary>
        /// Top up or deduct loyalty points from the card
        /// </summary>
        /// <param name="merchantID">Unique ID of the merchant issuing the card</param>
        /// <param name="points">Number of points to top up/deduct</param>
        /// <param name="topup">True if it is a top up operation, False if it is a deduction operation</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        public bool topupDeductPointsP(short merchantID, int points, bool topup)
        {
            try
            {
                if (debug) Log.WriteLog("Invoke Method", "topupDeductPointsP(" + Convert.ToString(merchantID) + "," +
                       Convert.ToString(points) + "," + Convert.ToString(topup) + ")");

                if (readLoyaltyCardP(merchantID))
                {
                    short sector = getProgramSector(merchantID, 2);
                    if (!topup) points = points * -1;
                    if (editLoyaltyP(Convert.ToInt16(sector), 1, points, this._loyalty[0].expiryDate, merchantID, issueCardID.ToString()))
                        return true;
                }

                return false;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[topupDeductPointsP]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[topupDeductPointsP] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }

        }

        /// <summary>
        /// Top up or deduct loyalty points from the card
        /// </summary>
        /// <param name="merchantID">Unique ID of the merchant issuing the card</param>
        /// <param name="points">Number of points to top up/deduct</param>
        /// <param name="order">Order of the loyalty program to top up/deduct points from/to</param>
        /// <param name="topup">True if it is a top up operation, False if it is a deduction operation</param>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        public bool topupDeductPointsP(short merchantID, short order, int points, bool topup)
        {
            try
            {
                if (debug) Log.WriteLog("Invoke Method", "topupDeductPointsP(" + Convert.ToString(merchantID) + "," + Convert.ToString(order) + "," +
                       Convert.ToString(points) + "," + Convert.ToString(topup) + ")");

                if (readLoyaltyCardP(merchantID))
                {
                    short sector = getProgramSector(merchantID, 2, order);
                    if (sector != 0)
                    {
                        if (!topup) points = points * -1;
                        if (editLoyaltyP(Convert.ToInt16(sector), order, points, this._loyalty[0].expiryDate, merchantID, issueCardID.ToString()))
                            return true;
                    }
                    else
                    {
                        lastErr = "[topupDeductPointsP] Specified order does not exist";
                        return false;
                    }
                }

                return false;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[topupDeductPointsP]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[topupDeductPointsP] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }

        }
        /// <summary>
        /// Gets the 4 byte card ID of the MIFARE card
        /// </summary>
        /// <returns>True if successful and False if unsuccessful. Error message found by reading lastErr.</returns>
        public bool getCardIDP()
        {
            try
            {
                if (PrinterCardComm.RfidCardRequestAndReadNum())
                {
                    _cardID = CardComm.currCardID;
                    if (CardComm.cardType[0] == CardComm.MIFARE_1K[0] && CardComm.cardType[1] == CardComm.MIFARE_1K[1]) _cardType = MIFARE_1K;
                    else if (CardComm.cardType[0] == CardComm.MIFARE_4K[0] && CardComm.cardType[1] == CardComm.MIFARE_4K[1]) _cardType = MIFARE_4K;

                    return true;
                }
                else lastErr = CardComm.lastErr;
                return false;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[getCardIDP]:Exception", ex.Message + (ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message));
                lastErr = "[getCardIDP] " + ex.Message + (ex.InnerException == null ? "" : ":" + ex.InnerException.Message);
                return false;
            }
        }

        public Boolean addChargeTxnP(int txnID, string desc, int itemID, int qty, int roomID, int bedID, decimal amt, DateTime txnTime)
        {
            try
            {
                if (debug) Log.WriteLog("Invoke Method", "addChargeTxnP(" + Convert.ToString(txnID) + "," + desc + "," + Convert.ToString(itemID) + "," +
                       Convert.ToString(roomID) + "," + Convert.ToString(bedID) + "," + String.Format("{0:#,##0.00;-#,##0.00;Zero}", amt) +
                       Convert.ToString(qty) + "," + txnTime.ToString("yyyy-MM-dd HH:mm:ss") + ")");

                if (writeToDB)
                {
                    try
                    {

                        db.OpenConnection();
                        SqlCommand cmd = db.MakeCommand_SP("[dbo].[addChargeTxnP]");
                        cmd.Parameters.Add(new SqlParameter("@cardIDM", SqlDbType.VarChar));
                        cmd.Parameters["@cardIDM"].Value = cardID;
                        cmd.Parameters.Add(new SqlParameter("@txnID", SqlDbType.Int));
                        cmd.Parameters["@txnID"].Value = txnID;
                        cmd.Parameters.Add(new SqlParameter("@description", SqlDbType.VarChar));
                        cmd.Parameters["@description"].Value = desc;
                        cmd.Parameters.Add(new SqlParameter("@itemID", SqlDbType.Int));
                        cmd.Parameters["@itemID"].Value = itemID;
                        cmd.Parameters.Add(new SqlParameter("@qty", SqlDbType.Int));
                        cmd.Parameters["@qty"].Value = roomID;
                        cmd.Parameters.Add(new SqlParameter("@roomID", SqlDbType.Int));
                        cmd.Parameters["@roomID"].Value = roomID;
                        cmd.Parameters.Add(new SqlParameter("@bedID", SqlDbType.Int));
                        cmd.Parameters["@bedID"].Value = bedID;
                        cmd.Parameters.Add(new SqlParameter("@txnAmt", SqlDbType.Int));
                        cmd.Parameters["@txnAmt"].Value = amt * 100;
                        cmd.Parameters.Add(new SqlParameter("@txnDate", SqlDbType.DateTime));
                        cmd.Parameters["@txnDate"].Value = txnTime;

                        cmd.ExecuteNonQuery();
                    }
                    catch (SqlException ex)
                    {

                        if (debug) Log.WriteLog("Exception", "[addChargeTxnP]:SQL Exception", ex.Message + System.Environment.NewLine + (ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message));
                        lastErr = "[addChargeTxnP] : Error updating database - " + ex.Message;
                        return false;
                    }
                    finally
                    {
                        if (writeToDB) db.CloseConnection();
                    }
                }

                return true;
            }
            catch (Exception ex)
            {
                if (debug) Log.WriteLog("Exception", "[topupDeductPurseP]:Exception", ex.Message + ex.InnerException == null ? "" : ":" + System.Environment.NewLine + ex.InnerException.Message);
                lastErr = "[topupDeductPurseP] " + ex.Message + ex.InnerException == null ? "" : ":" + ex.InnerException.Message;
                return false;
            }
        }

    }
}
